Skip to content

Latest commit

 

History

History
101 lines (77 loc) · 3.06 KB

FUZZING.md

File metadata and controls

101 lines (77 loc) · 3.06 KB

Linkerd Proxy Fuzz Testing

The proxy is tested via fuzzing by way of OSS-Fuzz and we rely on 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 set up we use for each of the fuzz tests is automatically generated by cargo fuzz init (described here).

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:

#![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:

#[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:

# 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 here is an example of this.