3.1 KiB
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 structure for each of the fuzz tests is automatically generated by
cargo fuzz init. See cargo fuzz's
README.md 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:
#![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 is an example of this.