# Linkerd Proxy Fuzz Testing

The proxy is tested via fuzzing by way of
[OSS-Fuzz](https://github.com/google/oss-fuzz) and we rely on
[cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) as our underlying fuzzing
engine.

## Fuzz tests

### Folder structure

We place the fuzz tests into folders within the individual crates that the fuzz
tests target. For example, we have a fuzz test that that target the crate
`/linkerd/addr` and the code in `/linkerd/addr/src` and thus the fuzz test that
targets this crate is put in `/linkerd/addr/fuzz`.

The folder structure for each of the fuzz tests is automatically generated by
`cargo fuzz init`. See cargo fuzz's
[`README.md`](https://github.com/rust-fuzz/cargo-fuzz#cargo-fuzz-init) for more
information.

### Fuzz targets

The general idea behind the fuzz tests is to follow the testing set up of the
rest of the proxy. As such, we both have fuzz tests that resemble small
unit-test-like code pieces, as well as fuzz tests that resemble larger
integration-test-like code pieces.

#### Unit-test-like fuzzers

The code in `/linkerd/addr/fuzz/fuzz_targets/fuzz_target_1.rs` is an example of
a unit-test-like fuzzer:

```rust
#![no_main]

#[cfg(fuzzing)]
use libfuzzer_sys::fuzz_target;

#[cfg(fuzzing)]
fuzz_target!(|data: &[u8]| {
    let _ = tracing_subscriber::fmt::try_init();
    if let Ok(s) = std::str::from_utf8(data) {
        tracing::info!(data = ?s, "running with input");
        linkerd_addr::fuzz_logic::fuzz_addr_1(s);
    }
});
```

`fuzz_target` is the entrypoint of the fuzzer and is what the underlying fuzzing
engine cargo-fuzz will call with pseudo-random data in the `data` argument. The
fuzzer further calls into code in the `linkerd2_addr` module, which is defined
as follows:

```rust
#[cfg(fuzzing)]
pub mod fuzz_logic {
    use super::*;

    pub fn fuzz_addr_1(fuzz_data: &str) {
        if let Ok(addr) = Addr::from_str(fuzz_data) {
            addr.is_loopback();
            addr.to_http_authority();
            addr.is_loopback();
            addr.socket_addr();
        }

        if let Ok(name_addr) = NameAddr::from_str_and_port(fuzz_data, 1234) {
            name_addr.port();
            name_addr.as_http_authority();
        }
    }
}
```

We use this indirection of having fuzzing-related code in the modules themselves
to align with the scoping of the proxy code.

We wrap our fuzzing code in `[#cfg(fuzzing)]` to avoid shipping the fuzzing code
when build the release binaries.

We compile and run the above fuzzer with the following commands:

```shell
# Build the fuzzer
cd linkerd/addr
cargo +nightly fuzz build

# Run fuzzer
./fuzz/target/x86_64-unknown-linux-gnu/release/fuzz_target_1
```

This is also the sequence of commands to use for running the fuzzers locally.

#### Integration-test-like fuzzers

The larger fuzzers we keep follow a similar structural set up as to the
unit-test-like fuzzers, but are essentially just more substantial in nature. The
idea behind these fuzzers is to test end-to-end concepts more so than individual
components of the proxy.

The [inbound fuzzer](/linkerd/app/inbound/fuzz/fuzz_targets/fuzz_target_1.rs)
is an example of this.
