Skip to content

Commit

Permalink
[libfuzzer] First part of libfuzzer-chrome documentation.
Browse files Browse the repository at this point in the history
I expect to add 2 more documents in the follow up:

- clusterfuzz-libfuzzer integration documentation (build bots, corpus, status links, reports)
- reference (fuzzer_test reference, fuzzer options, dictionaries, etc.)

BUG=539572

Review URL: https://codereview.chromium.org/1809843002

Cr-Commit-Position: refs/heads/master@{#381847}
  • Loading branch information
aizatsky-chromium authored and Commit bot committed Mar 18, 2016
1 parent 13c44c5 commit a6f8629
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 0 deletions.
32 changes: 32 additions & 0 deletions testing/libfuzzer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Libfuzzer in Chrome

[g.co/libfuzzer-chrome]

This directory contains integration between [LibFuzzer] and Chrome.
Libfuzzer is an in-process coverage-driven evolutionary fuzzer. It helps
engineers to uncover potential security & stability problems earlier.

*** note
**Requirements:** libfuzzer in chrome is supported with GN on Linux only.
***

## Integration Status

Fuzzer tests are well-integrated with Chrome build system & distributed
ClusterFuzz fuzzing system. Cover bug: [crbug.com/539572].

## Documentation

* [Getting Started Guide] walks you through all the steps necessary to create
your fuzzer and submit it to ClusterFuzz.
* [Efficient Fuzzer Guide] explains how to measure fuzzer effectiveness and
ways to improve it.
* [ClusterFuzz Integration] describes integration between ClusterFuzz and
libfuzzer.


[LibFuzzer]: http://llvm.org/docs/LibFuzzer.html
[crbug.com/539572]: https://bugs.chromium.org/p/chromium/issues/detail?id=539572
[Getting Started Guide]: ./getting_started.md
[Efficient Fuzzer Guide]: ./efficient_fuzzer.md

7 changes: 7 additions & 0 deletions testing/libfuzzer/clusterfuzz.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Libfuzzer and ClusterFuzz Integration



## Fuzzer Status

fuzzer status goes here.
94 changes: 94 additions & 0 deletions testing/libfuzzer/efficient_fuzzer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Efficient Fuzzer

This document describes ways to determine your fuzzer efficiency and ways
to improve it.

## Overview

Being a coverage-driven fuzzer, Libfuzzer considers a certain input *interesting*
if it results in new coverage. The set of all interesting inputs is called
*corpus*.
Items in corpus are constantly mutated in search of new interesting input.
Corpus is usually maintained between multiple fuzzer runs.

There are several metrics you should look at to determine your fuzzer effectiveness:

* fuzzer speed (exec/s)
* corpus size
* coverage

You can collect these metrics manually or take them from [ClusterFuzz status]
pages.

## Fuzzer Speed

Fuzzer speed is printed while fuzzer runs:

```
#19346 NEW cov: 2815 bits: 1082 indir: 43 units: 150 exec/s: 19346 L: 62
```

Because Libfuzzer performs randomized search, it is critical to have it as fast
as possible. You should try to get to at least 1,000 exec/s. Profile the fuzzer
using any standard tool to see where it spends its time.

### Initialization/Cleanup

Try to keep your fuzzing function as simple as possible. Prefer to use static
initialization and shared resources rather than bringing environment up and down
every single run.

Fuzzers don't have to shutdown gracefully (we either kill them or they crash
because sanitizer has found a problem). You can skip freeing static resource.

Of course all resources allocated withing `LLVMFuzzerTestOneInput` function
should be deallocated since this function is called millions of times during
one fuzzing session.


## Corpus Size

After running for a while the fuzzer would reach a plateau and won't discover
new interesting input. Corpus for a reasonably complex functionality
should contain hundreds (if not thousands) of items.

Too small corpus size indicates some code barrier that
libfuzzer is having problems penetrating. Common cases include: checksums,
magic numbers etc. The easiest way to diagnose this problem is to generate a
[coverage report](#Coverage). To fix the issue you can:

* change the code (e.g. disable crc checks while fuzzing)
* prepare fuzzer dictionary
* prepare [corpus seed](#Corpus-Seed).

## Coverage

You can easily generate source-level coverage report for a given corpus:

```
ASAN_OPTIONS=coverage=1:html_cov_report=1:sancov_path=./third_party/llvm-build/Release+Asserts/bin/sancov \
./out/libfuzzer/my_fuzzer -runs=0 ~/tmp/my_fuzzer_corpus
```

This will produce an .html file with colored source-code. It can be used to
determine where your fuzzer is "stuck".

## Corpus Seed

You can pass a corpus directory to a fuzzer that you run manually:

```
./out/libfuzzer/my_fuzzer ~/tmp/my_fuzzer_corpus
```

The directory can initially be empty. The fuzzer would store all the interesting
items it finds in the directory. You can help the fuzzer by "seeding" the corpus:
simply copy interesting inputs for your function to the corpus directory before
running. This works especially well for file-parsing functionality: just
use some valid files from your test suite.

After discovering new and interesting items, [upload corpus to Clusterfuzz].


[ClusterFuzz status]: ./clusterfuzz.md#Fuzzer-Status
[upload corpus to Clusterfuzz]: ./clusterfuzz.md#Upload-Corpus
119 changes: 119 additions & 0 deletions testing/libfuzzer/getting_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Getting Started with Libfuzzer in Chrome

*** note
**Prerequisites:** libfuzzer in chrome is supported with GN on Linux only.
***

This document will walk you through:

* setting up your build enviroment.
* creating your first fuzzer.
* running the fuzzer and verifying its vitals.

## Check Out ToT Clang

Libfuzzer relies heavily on compile-time instrumentation. Because it is still
under heavy development you need to use tot clang with libfuzzer:

```bash
# In chrome/src
LLVM_FORCE_HEAD_REVISION=1 ./tools/clang/scripts/update.py --force-local-build --without-android
```

To revert this run the same script without specifying `LLVM_FORCE_HEAD_REVISION`.

## Configure Build

Use `use_libfuzzer` GN argument together with sanitizer to generate build files:

```bash
# With address sanitizer
gn gen out/libfuzzer '--args=use_libfuzzer=true is_asan=true enable_nacl=false' --check
```

Supported sanitizer configurations are:

| GN Argument | Description |
|--------------|----|
| is_asan=true | enables [Address Sanitizer] to catch problems like buffer overruns. |
| is_msan=true | enables [Memory Sanitizer] to catch problems like uninitialed reads. |


## Write Fuzzer Function

Create a new .cc file and define a `LLVMFuzzerTestOneInput` function:

```cpp
extern "C" int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) {
// put your fuzzing code here and use data+size as input.
return 0;
}
```
[url_parse_fuzzer.cc] is a simple example of real-world fuzzer.
## Define GN Target
Define `fuzzer_test` GN target:
```
import("//testing/libfuzzer/fuzzer_test.gni")
fuzzer_test("my_fuzzer") {
sources = [ "my_fuzzer.cc" ]
deps = [ ... ]
}
```
## Build and Run Fuzzer Locally
Build with ninja as usual and run:
```bash
ninja -C out/libfuzzer url_parse_fuzzer
./out/libfuzzer url_parse_fuzzer
```

Your fuzzer should produce output like this:

```
INFO: Seed: 1787335005
INFO: -max_len is not provided, using 64
INFO: PreferSmall: 1
#0 READ units: 1 exec/s: 0
#1 INITED cov: 2361 bits: 95 indir: 29 units: 1 exec/s: 0
#2 NEW cov: 2710 bits: 359 indir: 36 units: 2 exec/s: 0 L: 64 MS: 0
#3 NEW cov: 2715 bits: 371 indir: 37 units: 3 exec/s: 0 L: 64 MS: 1 ShuffleBytes-
#5 NEW cov: 2728 bits: 375 indir: 38 units: 4 exec/s: 0 L: 63 MS: 3 ShuffleBytes-ShuffleBytes-EraseByte-
#6 NEW cov: 2729 bits: 384 indir: 38 units: 5 exec/s: 0 L: 10 MS: 4 ShuffleBytes-ShuffleBytes-EraseByte-CrossOver-
#7 NEW cov: 2733 bits: 424 indir: 39 units: 6 exec/s: 0 L: 63 MS: 1 ShuffleBytes-
#8 NEW cov: 2733 bits: 426 indir: 39 units: 7 exec/s: 0 L: 63 MS: 2 ShuffleBytes-ChangeByte-
#11 NEW cov: 2733 bits: 447 indir: 39 units: 8 exec/s: 0 L: 33 MS: 5 ShuffleBytes-ChangeByte-ChangeASCIIInt-ChangeBit-CrossOver-
#12 NEW cov: 2733 bits: 451 indir: 39 units: 9 exec/s: 0 L: 62 MS: 1 CrossOver-
#16 NEW cov: 2733 bits: 454 indir: 39 units: 10 exec/s: 0 L: 61 MS: 5 CrossOver-ChangeBit-ChangeBit-EraseByte-ChangeBit-
#18 NEW cov: 2733 bits: 458 indir: 39 units: 11 exec/s: 0 L: 24 MS: 2 CrossOver-CrossOver-
```

The `... NEW ...` line appears when libfuzzer finds new and interesting input. The
efficient fuzzer should be able to finds lots of them rather quickly.

The '... pulse ...' line will appear periodically to show the current status.


## Submitting Fuzzer to ClusterFuzz

ClusterFuzz builds and executes all `fuzzer_test` targets in the source tree.
The only thing you should do is to submit a fuzzer into Chrome.

## Next Steps

* After your fuzzer is submitted, you should check its [ClusterFuzz status] in
a day or two.
* Check the [Efficient Fuzzer Guide] to better understand your fuzzer
performance and for optimization hints.


[Address Sanitizer]: http://clang.llvm.org/docs/AddressSanitizer.html
[Memory Sanitizer]: http://clang.llvm.org/docs/MemorySanitizer.html
[url_parser_fuzzer.cc]: https://code.google.com/p/chromium/codesearch#chromium/src/testing/libfuzzer/fuzzers/url_parse_fuzzer.cc
[ClusterFuzz status]: ./clusterfuzz.md#Fuzzer-Status
[Efficient Fuzzer Guide]: ./efficient_fuzzer.md

0 comments on commit a6f8629

Please sign in to comment.