Skip to content

Commit 7896fe9

Browse files
authored
Add pages for pure concolic and hybrid fuzz testing (#3)
* Add instructions for hybrid fuzzing * Rename use cases to recipes Also, Improve wording for the fuzzing page * Add a recipe for cargo * Add a page for diverging input generation * Add pure concolic testing to the book
1 parent df68970 commit 7896fe9

File tree

7 files changed

+188
-8
lines changed

7 files changed

+188
-8
lines changed

docs/src/SUMMARY.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
# User Guide
77

88
- [Getting Started](./user_guide/getting_started.md)
9-
- [Use Cases](./user_guide/use_cases/section.md)
10-
- [Hybrid Fuzzing](./user_guide/hybrid_fuzzing.md)
9+
- [Recipes](./user_guide/recipes/section.md)
10+
- [Building Cargo Packages](./user_guide/recipes/cargo.md)
11+
- [Diverging Input Generation](./user_guide/recipes/div_input.md)
12+
- [Fuzzing](./user_guide/recipes/fuzzing.md)
1113
- [Configurations](./user_guide/configs.md)
1214

1315
# Technical Reference
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Building Cargo Packages
2+
3+
As mentioned before, you can look at `leafc` as a wrapper around `rustc` and
4+
it should be work in any command using `rustc`.
5+
6+
Building packages is no exception. As supported by `cargo` you can set
7+
[`build.rust`](https://doc.rust-lang.org/cargo/reference/config.html#buildrustc) configuration key
8+
to override the compiler.
9+
```bash
10+
$ export RUSTC=leafc
11+
$ cargo build
12+
```
13+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Diverging Input Generation
2+
3+
Each instance of concolic execution of a program, records a trace of the constraints put on symbolic variables at each
4+
step of the execution.
5+
Conditional branches are the major source of these constraints and whether they are held or not
6+
determines the target of the branch.
7+
Therefore, concolic execution can be used to find concrete values for the symbolic variables
8+
such that the execution diverges at conditional branches compared to the previous execution.
9+
We refer to these concrete values as diverging inputs, counterexamples, or generally answers.
10+
11+
The current default configuration of Leaf tries to find a diverging input whenever
12+
a new constraint is observed.
13+
For instance, in the following program, the input (`x = 10`) does not satisfy
14+
the branch condition (`x < 5`), which the backend reports as `{!(<(<Var1: u8>, 5u8))}`.
15+
It tries to find an input that would satisfy it (so the execution would diverge at this point),
16+
and reports back value `0u8` as a possible one.
17+
18+
## Diverging Standard Input Generation
19+
20+
If all symbolic variables are from `u8`, the default configuration puts the found answers
21+
in binary files in which each byte corresponds to a symbolic variable ordered by
22+
when they were marked as symbolic.
23+
24+
This is mainly meant for situations where we want to mark a file symbolic, e.g., standard input.
25+
26+
Although the backend (and execution of the instrumented program) is enough to obtain the diverging standard input,
27+
the one-time orchestrator is provided to facilitate this process with further control.
28+
29+
You can install it by running the following command in Leaf's root folder.
30+
```console
31+
leaf$ cargo install --path ./orchestrator
32+
```
33+
Then you provide the path to the instrumented program and the desired path to put the diverging inputs at.
34+
For example,
35+
```console
36+
$ leafo_onetime --program ./hello_world --outdir ./next
37+
next/diverging_0.bin
38+
```
39+
It runs the target program, and prints the names of the files generated as diverging input.
40+
41+
42+
## Pure Concolic Testing
43+
44+
By repeating the above procedure for each generated input, more possible paths
45+
in the target program will be covered. We use the term *pure concolic* testing,
46+
as used by [SymCC](https://github.com/eurecom-s3/symcc) for this method of testing.
47+
48+
If you are interested in finding possible crashes in your program using pure concolic testing,
49+
the project comes with a utility named `leaff_pure_conc`, which runs the loop for programs and captures those that cause a crash
50+
in them. Currently, it only supports programs with symbolic standard input (uses the one-time orchestrator).
51+
52+
To run pure concolic testing loop for a target:
53+
1. Install the tool.
54+
```console
55+
leaf$ cargo install --path ./integration/libafl/fuzzers/pure_concolic
56+
```
57+
1. Build your program with `leafc`.
58+
1. Pass the executable path to the tool. e.g.,
59+
```console
60+
$ leaff_pure_conc --conc-program ./hello_world
61+
```
62+
1. The loop stops if no more new distinct input is found to be given to the program (can possibly run forever).
63+
64+
We recommend looking at the options available for tuning the loop by passing `--help`.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Fuzzing
2+
3+
One of the use cases of concolic execution, which is demonstrated to be effective,
4+
is hybrid fuzzing, in which fuzzing is aided with solver-found inputs generated by symbolic
5+
execution to take certain paths inside the program that other techniques are inefficient
6+
to find.
7+
8+
Leaf is aimed to be suitable for this purpose and comes with a built-in support for [LibAFL][1],
9+
a customizable fuzzing framework with modern architecture written in Rust.
10+
Crate `libafl_leaf` contains facilities for using Leaf-instrumented programs with
11+
the fuzzers written using this library.
12+
13+
## Hybrid Fuzzing for libFuzzer
14+
15+
In an abstract manner, hybrid fuzzing for LibAFL-based fuzzers is achievable using
16+
a stage that generates diverging inputs from the current test case.
17+
This stage should perform the concolic execution using the current test case to derive the diverging inputs
18+
and offer them to the fuzzer for evaluation.
19+
Thus, the following steps are presumable for an execution-based concolic executor like Leaf.
20+
1. Build an executable equivalent to the fuzz target, which is suitable for concolic execution.
21+
1. Define a mutator stage that runs the built executable and obtains new inputs.
22+
1. Add the stage to the fuzzer.
23+
24+
The mentioned ingredients are provided by Leaf;
25+
`leafc` instruments your target program,
26+
`leafo_onetime` helps with collecting the diverging inputs,
27+
and `libafl_leaf` provides the stage.
28+
As `libFuzzer` (through `cargo-fuzz`) is one the most-used tools to perform fuzzing
29+
for Rust projects, a rudimentary support is also provided for harnesses
30+
written based on [libfuzzer-sys](https://github.com/rust-fuzz/libfuzzer)
31+
to upgrade them to a hybrid fuzzer.
32+
It is developed as an extension of [LibAFL][1]'s implementation of `libFuzzer`, so
33+
the [same instructions and options](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_libfuzzer)
34+
apply.
35+
36+
### Recipe
37+
38+
With an understanding of the general procedure above, you can follow the instruction
39+
below to upgrade your existing fuzzer to a hybrid one.
40+
41+
* Prerequisites
42+
1. The one-time orchestrator (`leafo_onetime`) is installed in your environment.
43+
If not, install it similarly to `leafc` using the following command in Leaf's root folder.
44+
```console
45+
leaf$ cargo install --path ./orchestrator
46+
```
47+
48+
1. You have a fuzzer written using `libfuzzer-sys`.
49+
We assume it is named `fuzz_target_1` and has the following template.
50+
```rust
51+
#![no_main]
52+
53+
use libfuzzer_sys::fuzz_target;
54+
55+
fuzz_target!(|data: &[u8]| {
56+
// fuzzed code goes here
57+
});
58+
```
59+
60+
1. Replace `libfuzzer-sys` source to Leaf's implementation in `Cargo.toml` of your fuzz project.
61+
```toml
62+
# From
63+
libfuzzer-sys = { version = "...", features = ["your", "features", "here"] }
64+
65+
# To
66+
libfuzzer-sys = { git = "https://github.com/sfu-rsl/leaf.git", package = "libafl_libfuzzer", features = ["your", "features", "here"]}
67+
```
68+
69+
1. Change `fuzz_target` macro invocation to `hybrid_fuzz_target`, and make
70+
`no_main` attribute conditional based on compilation with `leafc`.
71+
```rust
72+
#![cfg_attr(not(leafc), no_main)]
73+
74+
use libfuzzer_sys::hybrid_fuzz_target;
75+
76+
hybrid_fuzz_target!(|data: &[u8]| {
77+
// fuzzed code goes here
78+
});
79+
```
80+
(`hybrid_fuzz_target` additionally writes a program with a `main` function that reads
81+
the whole standard input, marks it as symbolic, and passes to the closure.)
82+
83+
1. Build your fuzz target with `leafc` in a *separate* cargo target directory like below.
84+
```console
85+
fuzz$ RUSTC=leafc cargo build --bin fuzz_target_1 --target-dir ./target/leaf
86+
```
87+
88+
1. Build your fuzzer normally, e.g.,
89+
```console
90+
fuzz$ cargo fuzz build fuzz_target_1
91+
```
92+
93+
1. Run your fuzzer with the additional argument `conc_program` which points to the
94+
instrumented executable built using `leafc`.
95+
```console
96+
fuzz$ cargo fuzz run fuzz_target_1 -- -conc_program=./target/leaf/debug/fuzz_target_1
97+
```
98+
99+
-----------------
100+
101+
Please refer to the technical documentation for further details about the components and steps mentioned above.
102+
103+
104+
[1]: https://github.com/AFLplusplus/LibAFL
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Recipes
2+
3+
(TBD)

docs/src/user_guide/use_cases/hybrid_fuzzing.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

docs/src/user_guide/use_cases/section.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)