Skip to content
This repository was archived by the owner on Aug 4, 2020. It is now read-only.

Commit a2871b7

Browse files
author
Yevgeny Pats
committedAug 9, 2019
Initial example for Rust libFuzzer/Cargo fuzz
0 parents  commit a2871b7

File tree

9 files changed

+277
-0
lines changed

9 files changed

+277
-0
lines changed
 

‎.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock
4+
.idea

‎.travis.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
dist: bionic
2+
language: rust
3+
rust:
4+
- nightly
5+
6+
services:
7+
- docker
8+
9+
env:
10+
global:
11+
# This is FUZZIT_API_KEY. see https://docs.travis-ci.com/user/environment-variables/#encrypting-environment-variables for how to create secrets in Travis.
12+
secure: "cInWOLG78+lZXfr6IsAT8CBixXVFdjW2eIS9gJuRWsNlkc8IR3UTzqMP1BaoeEcXIPKGmZALO0oYWi0NueSfKIh0D8RsSb7jCNU76sdPJh1RyJcKIQYWDzTVGj4W/jEq25DupFs8I5HZD/ItAs3vVu7Cl2Yn+Tjh9Gbb/dxEhkt6VvN48uEN1ZtYxKBualZATGHcFSt2Kv8o0cV0gDPg+RapmUmRS+l7XbUl/vlvyrTcbDLtsaftqHIrV0UWtvu13ZKSbG2iT/LIRBsIeSzzVG5C5TzwZMk7ASpDEfcEw4pnjw/KKBIdXiKmYMDl6RrPLRC3+kecyRJhlyfo8fLIEan0Bc8ZVhCEUeavXO9Nk1cMjTgpPuB9H8nbgFR6En5bpFo1VaFSGFiVCHre0p4JY5/tDQVo2An+sC2sJsRXYqo65k33fxyazjLXHH7uLpNAMro5IZGK30QHshsH0aoMGeEtRYlelqhg88P49drcyRCzwy6AA4Tm94Wm4K9fkKgJMpoZ/9JaoeBcIumTmHB+M8DmK86xLDdm8yiVqkz/Ve5jl8yRcdgyUYqOPzmAKNvzggLYh1Bsnx2yq9N+C+jgDJgaUZXV/XWLSfVm3i6NOIH8YfpJ7yoDr22IwZEtcRjLRGjQJyyHcu+ZC6Qm/xzMmpUhRhMXXjQZCOgaKhztc7Y="
13+
14+
jobs:
15+
include:
16+
- stage: Build, Unit-Tests & Fuzz-Tests
17+
go: 1.12.x
18+
script:
19+
- cargo install cargo-fuzz
20+
- ./ci/fuzzit.sh regression
21+
22+
- stage: Fuzzit (Fuzzing)
23+
if: branch = master AND type IN (push)
24+
go: 1.12.x
25+
script:
26+
- cargo install cargo-fuzz
27+
- ./ci/fuzzit.sh fuzzing

‎Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "example-rust"
3+
version = "0.1.0"
4+
authors = ["Yevgeny Pats <yp@fuzzit.dev>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]

‎README.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
[![Build Status](https://travis-ci.org/fuzzitdev/example-go.svg?branch=master)](https://travis-ci.org/fuzzitdev/example-rust)
2+
[![fuzzit](https://app.fuzzit.dev/badge?org_id=fuzzitdev&branch=master)](https://fuzzit.dev)
3+
4+
# Continuous Rust Fuzzing Example
5+
6+
This is an example of how to integrate your [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) targets with
7+
[Fuzzit](https://fuzzit.dev) Continuous Fuzzing Platform (Rust support is currently in Alpha).
8+
9+
This example will show the following steps:
10+
* [Building and running locally a simple cargo-fuzz target](#building--running-the-fuzzer)
11+
* [Integrate the cargo-fuzz target with Fuzzit via Travis-CI](#integrating-with-fuzzit-from-ci)
12+
13+
Result:
14+
* Fuzzit will run the fuzz targets continuously on daily basis with the latest release.
15+
* Fuzzit will run regression tests on every pull-request with the generated corpus and crashes to catch bugs early on.
16+
17+
Fuzzing for Rust can both help find complex bugs as well as correctness bugs. Rust is a safe language so memory corruption bugs
18+
are very unlikely to happen but some bugs can still have security implications.
19+
20+
This tutorial is less about how to build cargo-fuzz targets but more about how to integrate the targets with Fuzzit. A lot of
21+
great information is available at the [cargo-fuzz](https://rust-fuzz.github.io/book/cargo-fuzz.html) repository.
22+
23+
### Understanding the bug
24+
25+
The bug is located at `src/lib.rs` with the following code
26+
27+
```rust
28+
29+
pub fn parse_complex(data: &[u8]) -> bool{
30+
if data.len() == 5 {
31+
if data[0] == b'F' && data[1] == b'U' && data[2] == b'Z' && data[3] == b'Z' && data[4] == b'I' && data[5] == b'T' {
32+
return true
33+
}
34+
}
35+
return true;
36+
}
37+
```
38+
39+
This is the simplest example to demonstrate a classic off-by-one/out-of-bound error which causes the program to crash.
40+
Instead of `len(data) == 5` the correct code will be `len(data) == 6`.
41+
42+
### Understanding the fuzzer
43+
44+
the fuzzer is located at `fuzz/fuzz_targets/fuzz_target_1.rs` with the following code:
45+
46+
```rust
47+
48+
fuzz_target!(|data: &[u8]| {
49+
let _ = example_rust::parse_complex(&data);
50+
});
51+
52+
```
53+
54+
### Building & Running the fuzzer
55+
56+
Cargo fuzz required the nightly compiled ar describe in the cargo fuzz [book](https://rust-fuzz.github.io/book/cargo-fuzz.html)
57+
58+
```bash
59+
cargo fuzz run fuzz_target_1
60+
```
61+
62+
63+
Will print the following output and stacktrace:
64+
65+
```text
66+
INFO: Seed: 3265732669
67+
INFO: Loaded 1 modules (480 guards): 480 [0x100da12d8, 0x100da1a58),
68+
INFO: 6 files found in /Users/yevgenyp/PycharmProjects/example-rust/fuzz/corpus/fuzz_target_1
69+
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
70+
INFO: seed corpus: files: 6 min: 1b max: 5b total: 26b rss: 27Mb
71+
#7 INITED cov: 87 ft: 87 corp: 5/21b lim: 4 exec/s: 0 rss: 27Mb
72+
#262144 pulse cov: 87 ft: 87 corp: 5/21b lim: 261 exec/s: 131072 rss: 51Mb
73+
thread '<unnamed>' panicked at 'index out of bounds: the len is 5 but the index is 5', /Users/yevgenyp/PycharmProjects/example-rust/src/lib.rs:17:101
74+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
75+
==84593== ERROR: libFuzzer: deadly signal
76+
#0 0x1025ae445 in __sanitizer_print_stack_trace (lib__rustc__clang_rt.asan_osx_dynamic.dylib:x86_64+0x4c445)
77+
#1 0x100d23b12 in fuzzer::PrintStackTrace() FuzzerUtil.cpp:206
78+
#2 0x100d0756a in fuzzer::Fuzzer::CrashCallback() FuzzerLoop.cpp:237
79+
#3 0x100d0750d in fuzzer::Fuzzer::StaticCrashSignalCallback() FuzzerLoop.cpp:209
80+
#4 0x100d50a07 in fuzzer::CrashHandler(int, __siginfo*, void*) FuzzerUtilPosix.cpp:36
81+
#5 0x7fff69804b5c in _sigtramp (libsystem_platform.dylib:x86_64+0x4b5c)
82+
#6 0x106db5b75 in dyld::fastBindLazySymbol(ImageLoader**, unsigned long) (dyld:x86_64+0x4b75)
83+
#7 0x7fff696be6a5 in abort (libsystem_c.dylib:x86_64+0x5b6a5)
84+
#8 0x100d79288 in panic_abort::__rust_start_panic::abort::h15c0489ebcc623d0 lib.rs:48
85+
#9 0x100d79278 in __rust_start_panic lib.rs:44
86+
#10 0x100d78b98 in rust_panic panicking.rs:526
87+
#11 0x100d78b79 in std::panicking::rust_panic_with_hook::h111bdf4b9efb2f62 panicking.rs:497
88+
#12 0x100d7858c in std::panicking::continue_panic_fmt::ha408c1f6b7a89584 panicking.rs:384
89+
#13 0x100d78478 in rust_begin_unwind panicking.rs:311
90+
#14 0x100d8b3d1 in core::panicking::panic_fmt::h22e65e952cbe8c74 panicking.rs:85
91+
#15 0x100d8b388 in core::panicking::panic_bounds_check::h3ed7e9d8bf4f5005 panicking.rs:61
92+
#16 0x100cf963e in example_rust::parse_complex::h2ee809da6efcf96d lib.rs:17
93+
#17 0x100cf82aa in rust_fuzzer_test_input fuzz_target_1.rs:6
94+
#18 0x100d048c5 in libfuzzer_sys::test_input_wrap::_$u7b$$u7b$closure$u7d$$u7d$::h39216f33af358cfa lib.rs:11
95+
#19 0x100d0022c in std::panicking::try::do_call::h99bafe87b57c13d6 panicking.rs:296
96+
#20 0x100d7926b in __rust_maybe_catch_panic lib.rs:28
97+
#21 0x100cff9fc in std::panicking::try::he224cd8d43f275c5 panicking.rs:275
98+
#22 0x100cfe3a5 in std::panic::catch_unwind::hdccdbf00115971fe panic.rs:394
99+
#23 0x100d04419 in LLVMFuzzerTestOneInput lib.rs:9
100+
#24 0x100d09111 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:576
101+
#25 0x100d087b9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:485
102+
#26 0x100d0ac78 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:713
103+
#27 0x100d0bfb1 in fuzzer::Fuzzer::Loop(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, fuzzer::fuzzer_allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&) FuzzerLoop.cpp:844
104+
#28 0x100d3f4bb in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:765
105+
#29 0x100d61629 in main FuzzerMain.cpp:20
106+
#30 0x7fff696193d4 in start (libdyld.dylib:x86_64+0x163d4)
107+
108+
NOTE: libFuzzer has rudimentary signal handlers.
109+
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
110+
SUMMARY: libFuzzer: deadly signal
111+
MS: 1 ChangeByte-; base unit: e4fd1292391a997176aa1c86db666f2d5d48fb90
112+
0x46,0x55,0x5a,0x5a,0x49,
113+
FUZZI
114+
artifact_prefix='/Users/yevgenyp/PycharmProjects/example-rust/fuzz/artifacts/fuzz_target_1/'; Test unit written to /Users/yevgenyp/PycharmProjects/example-rust/fuzz/artifacts/fuzz_target_1/crash-df779ced6b712c5fca247e465de2de474d1d23b9
115+
Base64: RlVaWkk=
116+
```
117+
118+
## Integrating with Fuzzit from CI
119+
120+
The best way to integrate with Fuzzit is by adding a two stages in your Contintous Build system
121+
(like Travis CI or Circle CI).
122+
123+
Fuzzing stage:
124+
125+
* build a fuzz target
126+
* download `fuzzit` cli
127+
* authenticate with `fuzzit auth`
128+
* create a fuzzing job by uploading fuzz target
129+
130+
Regression stage
131+
* build a fuzz target
132+
* download `fuzzit` cli
133+
* authenticate with `fuzzit auth`
134+
* create a local regression fuzzing job - This will pull all the generated corpus and run them through
135+
the fuzzing binary. If new bugs are introduced this will fail the CI and alert
136+
137+
here is the relevant snippet from the [./ci/fuzzit.sh](https://github.com/fuzzitdev/example-rust/blob/master/ci/fuzzit.sh)
138+
which is being run by [.travis.yml](https://github.com/fuzzitdev/example-rust/blob/master/.travis.yml)
139+
140+
```bash
141+
wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.0.0/fuzzit_Linux_x86_64
142+
chmod a+x fuzzit
143+
./fuzzit auth ${FUZZIT_API_KEY}
144+
./fuzzit create job --type $1 --branch $TRAVIS_BRANCH --revision $TRAVIS_COMMIT $TARGET_ID ./fuzzer
145+
```
146+
147+
NOTE: In production it is advised to download a pinned version of the [CLI](https://github.com/fuzzitdev/fuzzit)
148+
like in the example. In development you can use latest version:
149+
https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_${OS}_${ARCH}.
150+
Valid values for `${OS}` are: `Linux`, `Darwin`, `Windows`.
151+
Valid values for `${ARCH}` are: `x86_64` and `i386`.
152+
153+
The steps are:
154+
* Authenticate with the API key (you should keep this secret) you can find in the fuzzit settings dashboard.
155+
* Upload the fuzzer via create job command and create the fuzzing job. In This example we use two type of jobs:
156+
* Fuzzing job which is run on every push to master which continuous the previous job just with the new release.
157+
Continuous means all the current corpus is kept and the fuzzer will try to find new paths in the newly added code
158+
* In a Pull-Request the fuzzer will run a quick "sanity" test running the fuzzer through all the generated corpus
159+
and crashes to see if the Pull-Request doesnt introduce old or new crashes. This will be alred via the configured
160+
channel in the dashboard
161+
* The Target is not a secret. This ID can be retrieved from the dashboard after your create the appropriate target in the dashboard.
162+
Each target has it's own corpus and crashes.

‎ci/fuzzit.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
set -xe
2+
3+
if [ -z ${1+x} ]; then
4+
echo "must call with job type as first argument e.g. 'fuzzing' or 'sanity'"
5+
echo "see https://github.com/fuzzitdev/example-go/blob/master/.travis.yml"
6+
exit 1
7+
fi
8+
9+
## build fuzzer
10+
cargo fuzz run fuzz_parse_complex -- -runs=0
11+
12+
wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.2/fuzzit_Linux_x86_64
13+
chmod a+x fuzzit
14+
15+
if [ $1 == "fuzzing" ]; then
16+
./fuzzit auth ${FUZZIT_API_KEY}
17+
./fuzzit create job --branch $TRAVIS_BRANCH --revision $TRAVIS_COMMIT fuzzitdev/rust-parse-complex ./fuzz/target/x86_64-unknown-linux-gnu/debug/fuzz_parse_complex
18+
else
19+
./fuzzit create job --local fuzzitdev/rust-parse-complex ./fuzz/target/x86_64-unknown-linux-gnu/debug/fuzz_parse_complex
20+
fi

‎fuzz/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
target
3+
corpus
4+
artifacts

‎fuzz/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
[package]
3+
name = "example-rust-fuzz"
4+
version = "0.0.1"
5+
authors = ["Automatically generated"]
6+
publish = false
7+
8+
[package.metadata]
9+
cargo-fuzz = true
10+
11+
[dependencies.example-rust]
12+
path = ".."
13+
[dependencies.libfuzzer-sys]
14+
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
15+
16+
# Prevent this from interfering with workspaces
17+
[workspace]
18+
members = ["."]
19+
20+
[[bin]]
21+
name = "fuzz_parse_complex"
22+
path = "fuzz_targets/fuzz_parse_complex.rs"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![no_main]
2+
#[macro_use] extern crate libfuzzer_sys;
3+
extern crate example_rust;
4+
5+
fuzz_target!(|data: &[u8]| {
6+
let _ = example_rust::parse_complex(&data);
7+
});

‎src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use crate::parse_complex;
4+
5+
#[test]
6+
fn it_works() {
7+
assert_eq!(2 + 2, 4);
8+
}
9+
10+
fn it_works2() {
11+
assert_eq!(parse_complex(&[]), true);
12+
}
13+
}
14+
15+
pub fn parse_complex(data: &[u8]) -> bool{
16+
if data.len() == 5 {
17+
if data[0] == b'F' && data[1] == b'U' && data[2] == b'Z' && data[3] == b'Z' && data[4] == b'I' && data[5] == b'T' {
18+
return true
19+
}
20+
}
21+
return true;
22+
}

0 commit comments

Comments
 (0)
This repository has been archived.