/oss-fuzz-targets

Fuzz targets for OSS-Fuzz

Primary LanguageMakefile

Fuzz Targets

The files in this repository are fuzzing targets for wolfSSL. They follow the LLVM libFuzzer API and have a very specific naming scheme for integration with Google's OSS-Fuzz service. For more information about these qualities, see the section on making new targets below. To be run, they must be linked against libFuzzer.a. For more information about compilation, see the section on compiling below.

The very last section of this README is meant to document the foreseeable future of this repository, including suggestions on what fuzz targets should be written next. It is requested that future editors of this directory update that section to reflect their work and thoughts.

The first thing to know is that you only need to compile these yourself if you intend to do testing outside of the OSS-Fuzz environment, which is useful, as getting OSS-Fuzz up takes some time. After you have finished compiling, you may move on to the section on running fuzz targets below.

For information on how to get a target running inside of OSS-Fuzz, see the section on OSS-Fuzz below.

For the entirety of this section, let "fuzz_target" be a stand-in for the name of your target. It will be identical to the directory name containing the source code.

If you have access to the make utility, everything will be simple. Before compiling any individual target, run make deps. If necessary, make will automatically retrieve the libFuzzer library and the most recent version of clang, both of which are required for compilation. Neither are installed, but kept locally instead. make dependencies is synonymous.

From there, simply execute make fuzz_target to compile a fuzz target.

Furthermore, running make or make all will compile all fuzz targets, running make clean will delete the compiled fuzz targets but leave the source files intact, and running make spotless will act like make clean but also delete the dependencies build by make deps.

If you don't have access to make, you're going to have to do it all by hand. What follows for the rest of this section are the instructions for doing what the Makefile describes, but by yourself. If make is available and worked, you may skip the remainder of this section.

The first thing to know is that the fuzz targets in this directory are written in C, yet libFuzzer.a is a C++ library. As such, the compilation of a target comes in two fazes: compile, then link. Similarly, because of the nature of libFuzzer, compilation must be done through clang rather than gcc.

The official libFuzzer documentation can be found on the LLVM website.

The first thing to do is to get libFuzzer. To do this, run these commands from the shell:

$ url=https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer
$ git clone $url Fuzzer
$ ./Fuzzer/build.sh
$ rm -rf Fuzzer

The above clones in the libFuzzer git repository then builds it. There should now be a libFuzzer.a in this directory.

Next we need an up-to-date version of clang. To do this, we're going to clone down some tools. Because of how those tools expect their directory hierarchy, we're going to put that repository three directories deep. After that, we'll use those tools to pull down the most up-to-date version of clang then make a link for our convenience. In all, that looks like this:

$ url=https://chromium.googlesource.com/chromium/src/tools/clang
$ git clone $url new_clang/clang/clang
$ python new_clang/clang/clang/scripts/update.py
$ ln -s -t . new_clang/third_party/llvm-build/Release+Asserts/bin/clang{,++}

And with that, you now have a recent version of clang. It has not been installed, so to use it you'll have to call clang like ./clang from this directory.

To compile, run these commands from the shell:

$ CFLAGS="-fsanitize=address -fsanitize-coverage=trace-pc-guard"
$ LDFLAGS="-L. -lwolfssl -lFuzzer"
$ ./clang $CFLAGS -c fuzz_target/target.c -o fuzz_target/target.o
$ ./clang++ $CFLAGS fuzz_target/target.o -o fuzz_target/target $LDFLAGS
$ rm fuzz_target.o

And with that, fuzz_target has been compiled. This compile step is the only

For the entirety of this section, let "fuzz_target" be a stand-in for the name of your target. It will be identical to the directory name containing the source code.

After compiling, cd into fuzz_target/, and target is an executable that can be called like this:

$ ./target [OPTION ...] [CORPUS ...]

or

$ ./target [OPTION ...] [FILE ...]

A corpus is a directory with files containing example input data, good or bad, for the fuzzer to use as a starting point. If a new file is generated, it will be placed in the first corpus listed. If files are passed, they will be passed without being fuzzed. This is useful for testing if a fuzz target modification worked.

Options come strictly in the form -flag=value. Here are some useful options:

  • -runs=N: run N tests. The default (N = -1) means to run indefinitely.
  • -max_len=N: cap input data at at most N bytes of data
  • -max_total_time=N: run tests for at most N seconds. The default (N = 0) means to run indefinitely.
  • -help=1: show help, including calling convention and all options

There are three different things that you should make every time you make a new fuzz target: source code, options, and corpus. See their respective subsections below for more information on each.

All three parts must be placed in the same directory, but separate from any other fuzz target. The name of this directory will be the name of the fuzz target. There are no exceptions; failure to comply with this will prevent your fuzz target from being found by OSS-Fuzz.

The only required part is the source code; options files and corpora may be omitted, though it is recommended that you at least also include a corpus.

The fourth, extra optional part is the dictionary. You can read more about how to use or include them in the OSS-Fuzz documentation. If you wish to include one, create a file ending in ".dict" in the root directory of this repository, then add a line like this to the options file for the fuzz target that will use it:

[libfuzzer]
dict = my_dictionary.dict

Note that dictionaries are found relative to the root directory of this repository. Do not use relative or absolute paths, simply the name of the file.

The source code is more or less what you'd expect: the code describing the test. It must be named target.c. Internally, the only requirement is that it not implement main() and instead implement a function for this prototype:

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz);

For definitions of uint8_t and size_t, include stdint.h and stdlib.h respectively.

In this function, data is a buffer of size sz bytes. Within this buffer is the content of a file. If the target was invoked at the command line with file names, the content of data will be exactly the content of the file. Otherwise, it will contain the fuzzed version of files in the corpus.

From here, data and sz must become the input of another function. In many cases, this is as simple as using data and sz in the function call, but sometimes creativity must be used to expose the library to the fuzzed data.

An options file will indicate to OSS-Fuzz which flags and values to pass to the fuzz target when running it. An options file must be named target.options.

The format seems to be similar to the ini format, though no explicit confirmation of this was found. For example, a file like this is valid:

[libfuzzer]
max_len = 1024

A corpus is a directory containing "seeds" for the fuzzer. The fuzzer will intelligently generate fuzzed input from these files to feed into the fuzz target. Inside, you can include good or bad input. In general, it is recommended that you include a few examples of good input. If ever fuzzing finds an example of bad input, it is also recommended that you add this bad input to make sure the problem that created it is not re-introduced.

The corpus must be a directory. It may have any name, though know that when OSS-Fuzz tries to find corpora, it will assume every directory in the target's directory is a corpus.

The contents of the corpus do not need to conform to any kind of naming scheme, though libFuzzer is happiest when the file's name is the sha1 sum of the file, and so it is recommended that all corpus files are named accordingly.

If you've followed all the conventions described in the section on writing new targets above, you can get any target you've written to run in OSS-Fuzz pretty simply.

Before trying to get a target working with OSS-Fuzz, it is recommended that you can confirm that it compiles and runs locally, because doing so makes the turn-around on fixing bugs/mistakes much shorter. For more information on compiling targets locally, see the section on compiling above.

The first thing to do is to make sure that Docker is installed and running. To do this, please consult Docker's website. If you are on a Linux system, it is probable that Docker is already in your distribution's software repository.

To get the OSS-Fuzz repository down onto your local machine, a git-clone like this should work:

$ git clone https://github.com/google/oss-fuzz

Note that if you are going to be modifying the fuzz targets, you'll need to modify where Docker will get its targets. Open ./oss-fuzz/projects/wolfssl/Dockerfile and change this line:

RUN git clone --depth 1 https://github.com/wolfssl/wolfssl.git wolfssl

to something like this:

RUN git clone --depth 1 https://github.com/<you>/wolfssl.git -b <work_branch> wolfssl

Be sure to replace <you> with your github user name and <work_branch> with the branch to which you are pushing your new targets.

From the OSS-Fuzz root directory, we're going to run some Python scripts. Take notice that these scripts are written in Python 2. Furthermore, you will probably need to run them with root permissions. Appending sudo should do. Finally, these commands require the Docker daemon to be running. Those commands should look like this:

# python infra/helper.py build_image wolfssl
# python infra/helper.py build_fuzzers --sanitizer address wolfssl

You may also choose to use memory or undefined for the sanitizer option.

To actually run a fuzz target, run this command:

# python infra/helper.py run_fuzzer wolfssl fuzz_target

Where fuzz_target stands for the fuzz target you wish to run. The name of the fuzz target corresponds with the directory name containing the source code of the target you wish to run.

Add more tests for various wolfSSL and wolfCrypt APIs. Focus on APIs which process buffers containing data that plausibly could originate from some outside source. It is possible that a target for any given API has already been written: check the private wolfssl testing repository for targets before writing them yourself.