Skip to content

Commit

Permalink
Add fuzzing via cargo-fuzz
Browse files Browse the repository at this point in the history
  • Loading branch information
YaLTeR committed Sep 5, 2019
1 parent cd6a6dc commit 088153f
Show file tree
Hide file tree
Showing 10 changed files with 323 additions and 7 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ rand = "0.7"
rand_chacha = "0.2"
semver = "0.9"

[target.'cfg(fuzzing)'.dependencies]
arbitrary = "0.2"
interpolate_name = "0.2.2"
rand = "0.7"
rand_chacha = "0.2"

[[bin]]
name = "rav1e"
required-features = ["binaries"]
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,22 @@ Run regular benchmarks with:
cargo bench --features=bench
```

### Fuzzing

Install `cargo-fuzz` with `cargo install cargo-fuzz`. Running fuzz targets requires nightly Rust, so install that too with `rustup install nightly`.

* List the fuzz targets with `cargo fuzz list`.
* Run a fuzz target with `cargo +nightly fuzz run <target>`.
* Parallel fuzzing: `cargo +nightly fuzz run --jobs <n> <target> -- -workers=<n>`.
* Disable memory leak detection (seems to trigger always): `cargo +nightly fuzz run <target> -- -detect_leaks=0`.
* Bump the "slow unit" time limit: `cargo +nightly fuzz run <target> -- -report_slow_units=600`.
* Make the fuzzer generate long inputs right away (useful because fuzzing uses a ring buffer for data, so when the fuzzer generates big inputs it has a chance to affect different settings individually): `cargo +nightly fuzz run <target> -- -max_len=256 -len_control=0`.
* Release configuration (not really recommended because it disables debug assertions and integer overflow assertions): `RUSTFLAGS='-C codegen-units=1' cargo +nightly fuzz run --release <target>`
* `codegen-units=1` fixes https://github.com/rust-fuzz/cargo-fuzz/issues/161.
* Just give me the complete command line: `RUSTFLAGS='-C codegen-units=1' cargo +nightly fuzz run -j10 encode -- -workers=10 -detect_leaks=0 -timeout=600 -report_slow_units=600 -max_len=256 -len_control=0`.
* Run a single artifact with debug output: `RUST_LOG=debug <path/to/fuzz/target/executable> <path/to/artifact>`, for example, `RUST_LOG=debug fuzz/target/x86_64-unknown-linux-gnu/debug/encode fuzz/artifacts/encode/crash-2f5672cb76691b989bbd2022a5349939a2d7b952`.
* For adding new fuzz targets, see comment at the top of `src/fuzzing.rs`.

## Getting in Touch

Come chat with us on the IRC channel #daala on Freenode! If you don't have IRC set
Expand Down
4 changes: 4 additions & 0 deletions fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

target
corpus
artifacts
35 changes: 35 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

[package]
name = "rav1e-fuzz"
version = "0.0.1"
authors = ["Automatically generated"]
publish = false

[package.metadata]
cargo-fuzz = true

[dependencies]
pretty_env_logger = "0.3"

[dependencies.rav1e]
path = ".."
features = ["decode_test_dav1d"]
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[[bin]]
name = "encode_decode"
path = "fuzz_targets/encode_decode.rs"
required-features = ["rav1e/decode_test_dav1d"]

[[bin]]
name = "encode"
path = "fuzz_targets/encode.rs"

[[bin]]
name = "construct_context"
path = "fuzz_targets/construct_context.rs"
10 changes: 10 additions & 0 deletions fuzz/fuzz_targets/construct_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate rav1e;
use rav1e::fuzzing::*;

fuzz_target!(|data| {
let _ = pretty_env_logger::try_init();

fuzz_construct_context(data)
});
10 changes: 10 additions & 0 deletions fuzz/fuzz_targets/encode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate rav1e;
use rav1e::fuzzing::*;

fuzz_target!(|data| {
let _ = pretty_env_logger::try_init();

fuzz_encode(data)
});
10 changes: 10 additions & 0 deletions fuzz/fuzz_targets/encode_decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate rav1e;
use rav1e::fuzzing::*;

fuzz_target!(|data| {
let _ = pretty_env_logger::try_init();

fuzz_encode_decode(data)
});
216 changes: 216 additions & 0 deletions src/fuzzing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use std::sync::Arc;

use arbitrary::*;

use crate::prelude::*;

// Adding new fuzz targets
//
// 1. Add a function to this file which looks like this:
//
// pub fn fuzz_something(data: &[u8]) {
// let mut g = create_generator!();
//
// // Invoke everything you need.
// //
// // You should use g.g() to get an arbitrary value of any type that
// // implements Arbitrary [1]. This is how fuzzer affects the
// // execution—by feeding in different bytes, which result in different
// // arbitrary values being generated.
// // [1]: https://docs.rs/arbitrary/0.2.0/arbitrary/trait.Arbitrary.html
// //
// // Print out the structures you create with arbitrary data with
// // debug!().
// }
//
// 2. cargo fuzz add something
// 3. Copy the contents of any other .rs file from fuzz/fuzz_targets/ into the
// newly created fuzz/fuzz_targets/something.rs and change the function
// being called to fuzz_something.
//
// Now you can fuzz the new target with cargo fuzz.

// A helper for generating arbitrary data.
struct Generator<'a> {
buffer: RingBuffer<'a>,
}

impl<'a> Generator<'a> {
fn new(data: &'a [u8]) -> Result<Self, BufferError> {
Ok(Self { buffer: RingBuffer::new(data, data.len())? })
}

fn g<T: Arbitrary>(&mut self) -> T {
<T as Arbitrary>::arbitrary(&mut self.buffer).unwrap()
}
}

macro_rules! create_generator {
($data:expr) => {{
let g = Generator::new($data);
if g.is_err() {
return;
}
g.unwrap()
}};
}

pub fn fuzz_construct_context(data: &[u8]) {
let mut g = create_generator!(data);

let mut config = Config::default();
config.threads = 1;
config.enc.width = g.g();
config.enc.height = g.g();
config.enc.bit_depth = (g.g::<u8>() % 17) as usize;
config.enc.still_picture = g.g();
config.enc.time_base = Rational::new(g.g(), g.g());
config.enc.min_key_frame_interval = g.g();
config.enc.max_key_frame_interval = g.g();
config.enc.reservoir_frame_delay = g.g();
config.enc.low_latency = g.g();
config.enc.quantizer = g.g();
config.enc.min_quantizer = g.g();
config.enc.bitrate = g.g();
config.enc.tile_cols = g.g();
config.enc.tile_rows = g.g();
config.enc.tiles = g.g();
config.enc.rdo_lookahead_frames = g.g();
config.enc.speed_settings = SpeedSettings::from_preset(g.g());
config.enc.show_psnr = g.g();
config.enc.train_rdo = g.g();

debug!("config = {:#?}", config);

let _: Result<Context<u16>, _> = config.new_context();
}

fn encode_frames(
ctx: &mut Context<u8>, mut frames: impl Iterator<Item = Frame<u8>>,
) -> Result<(), EncoderStatus> {
loop {
let rv = ctx.receive_packet();
debug!("ctx.receive_packet() = {:#?}", rv);

match rv {
Ok(_packet) => {}
Err(EncoderStatus::Encoded) => {}
Err(EncoderStatus::LimitReached) => {
break;
}
Err(EncoderStatus::NeedMoreData) => {
ctx.send_frame(frames.next().map(Arc::new))?;
}
Err(EncoderStatus::EnoughData) => {
unreachable!();
}
Err(EncoderStatus::NotReady) => {
unreachable!();
}
Err(EncoderStatus::Failure) => {
return Err(EncoderStatus::Failure);
}
}
}

Ok(())
}

pub fn fuzz_encode(data: &[u8]) {
let mut g = create_generator!(data);

let mut config = Config::default();
config.threads = 1;
config.enc.width = g.g::<u8>() as usize + 1;
config.enc.height = g.g::<u8>() as usize + 1;
config.enc.still_picture = g.g();
config.enc.time_base = Rational::new(g.g(), g.g());
config.enc.min_key_frame_interval = (g.g::<u8>() % 4) as u64;
config.enc.max_key_frame_interval = (g.g::<u8>() % 4) as u64 + 1;
config.enc.low_latency = g.g();
config.enc.quantizer = g.g();
config.enc.min_quantizer = g.g();
config.enc.bitrate = g.g();
// config.enc.tile_cols = g.g();
// config.enc.tile_rows = g.g();
// config.enc.tiles = g.g();
config.enc.rdo_lookahead_frames = g.g();
config.enc.speed_settings = SpeedSettings::from_preset(10);

debug!("config = {:#?}", config);

let res = config.new_context();
if res.is_err() {
return;
}
let mut context: Context<u8> = res.unwrap();

let frame_count = g.g::<u8>() % 3 + 1;
let frames = (0..frame_count).map(|_| {
let mut frame = Frame::new(
config.enc.width,
config.enc.height,
config.enc.chroma_sampling,
);

for plane in &mut frame.planes {
let stride = plane.cfg.stride;
for row in plane.data_origin_mut().chunks_mut(stride) {
for pixel in row {
*pixel = g.g();
}
}
}

frame
});

let _ = encode_frames(&mut context, frames);
}

#[cfg(feature = "decode_test_dav1d")]
pub fn fuzz_encode_decode(data: &[u8]) {
use crate::test_encode_decode::*;

let mut g = create_generator!(data);

let w = g.g::<u8>() as usize + 1;
let h = g.g::<u8>() as usize + 1;
let speed = 10;
let q = g.g();
let limit = (g.g::<u8>() % 3) as usize + 1;
let min_keyint = g.g::<u64>() % 4;
let max_keyint = g.g::<u64>() % 4 + 1;
let low_latency = g.g();
let bitrate = g.g();

debug!(
"w = {:#?}\n\
h = {:#?}\n\
speed = {:#?}\n\
q = {:#?}\n\
limit = {:#?}\n\
min_keyint = {:#?}\n\
max_keyint = {:#?}\n\
low_latency = {:#?}\n\
bitrate = {:#?}",
w, h, speed, q, limit, min_keyint, max_keyint, low_latency, bitrate
);

let mut dec = get_decoder::<u8>("dav1d", w, h);
dec.encode_decode(
w,
h,
speed,
q,
limit,
8,
Default::default(),
min_keyint,
max_keyint,
low_latency,
bitrate,
1,
1,
);
}
8 changes: 7 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ pub mod version {
format!("{} ({})", short(), semver)
}
}
#[cfg(all(test, any(feature = "decode_test", feature = "decode_test_dav1d")))]
#[cfg(all(
any(test, fuzzing),
any(feature = "decode_test", feature = "decode_test_dav1d")
))]
mod test_encode_decode;

#[cfg(feature = "bench")]
Expand Down Expand Up @@ -255,3 +258,6 @@ pub mod bench {
pub use crate::util::*;
}
}

#[cfg(fuzzing)]
pub mod fuzzing;
Loading

0 comments on commit 088153f

Please sign in to comment.