diff --git a/Cargo.lock b/Cargo.lock index 567fe7fd10..9843dc94d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,15 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +[[package]] +name = "arbitrary" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "698b65a961a9d730fb45b6b0327e20207810c9f61ee421b082b27ba003f49e2b" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "async-stream" version = "0.3.0" @@ -163,6 +172,17 @@ dependencies = [ "gzip-header", ] +[[package]] +name = "derive_arbitrary" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df89dd0d075dea5cc5fdd6d5df6b8a61172a710b3efac1d6bdb9dd8b78f82c1a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dyn-clone" version = "1.0.4" @@ -570,6 +590,16 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" +[[package]] +name = "libfuzzer-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86c975d637bc2a2f99440932b731491fc34c7f785d239e38af3addd3c2fd0e46" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libmimalloc-sys" version = "0.1.20" @@ -700,11 +730,13 @@ dependencies = [ name = "linkerd-app-inbound" version = "0.1.0" dependencies = [ + "arbitrary", "bytes", "futures", "http", "hyper", "indexmap", + "libfuzzer-sys", "linkerd-app-core", "linkerd-app-test", "linkerd-io", @@ -1384,9 +1416,11 @@ dependencies = [ name = "linkerd-transport-header" version = "0.1.0" dependencies = [ + "arbitrary", "async-trait", "bytes", "futures", + "libfuzzer-sys", "linkerd-dns-name", "linkerd-error", "linkerd-io", diff --git a/linkerd/app/inbound/Cargo.toml b/linkerd/app/inbound/Cargo.toml index cbf85e72bc..2b14683ba7 100644 --- a/linkerd/app/inbound/Cargo.toml +++ b/linkerd/app/inbound/Cargo.toml @@ -21,6 +21,8 @@ tracing = "0.1.23" [target.'cfg(fuzzing)'.dependencies] hyper = { version = "0.14.2", features = ["http1", "http2"] } linkerd-app-test = { path = "../test" } +arbitrary = { version = "1", features = ["derive"] } +libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] } [dependencies.tower] version = "0.4" diff --git a/linkerd/app/inbound/fuzz/fuzz_targets/fuzz_target_1.rs b/linkerd/app/inbound/fuzz/fuzz_targets/fuzz_target_1.rs index eda0fe81ab..9da3ddad8d 100644 --- a/linkerd/app/inbound/fuzz/fuzz_targets/fuzz_target_1.rs +++ b/linkerd/app/inbound/fuzz/fuzz_targets/fuzz_target_1.rs @@ -1,27 +1,12 @@ #![no_main] -use libfuzzer_sys::arbitrary::Arbitrary; use libfuzzer_sys::fuzz_target; +use linkerd_app_inbound::http::fuzz_logic::*; -#[derive(Debug, Arbitrary)] -struct TransportHeaderSpec { - uri: Vec, - header_name: Vec, - header_value: Vec, - http_method: bool, -} - -fuzz_target!(|inp: TransportHeaderSpec| { - if let Ok(uri) = std::str::from_utf8(&inp.uri[..]) { - if let Ok(header_name) = std::str::from_utf8(&inp.header_name[..]) { - if let Ok(header_value) = std::str::from_utf8(&inp.header_value[..]) { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(linkerd_app_inbound::http::fuzz_logic::fuzz_entry_raw( - uri, - header_name, - header_value, - inp.http_method, - )); - } - } +fuzz_target!(|requests: Vec| { + if requests.len() == 0 { + return; } + + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(fuzz_entry_raw(requests)); }); diff --git a/linkerd/app/inbound/src/http/mod.rs b/linkerd/app/inbound/src/http/mod.rs index 61995c56c4..4710e45f08 100644 --- a/linkerd/app/inbound/src/http/mod.rs +++ b/linkerd/app/inbound/src/http/mod.rs @@ -259,6 +259,7 @@ pub mod fuzz_logic { }; use hyper::http; use hyper::{client::conn::Builder as ClientBuilder, Body, Request, Response}; + use libfuzzer_sys::arbitrary::Arbitrary; use linkerd_app_core::{ io::{self, BoxedIo}, proxy, @@ -270,7 +271,15 @@ pub mod fuzz_logic { pub use linkerd_app_test as support; use linkerd_app_test::*; - pub async fn fuzz_entry_raw(uri: &str, header_name: &str, header_value: &str, is_get: bool) { + #[derive(Debug, Arbitrary)] + pub struct HttpRequestSpec { + pub uri: Vec, + pub header_name: Vec, + pub header_value: Vec, + pub http_method: bool, + } + + pub async fn fuzz_entry_raw(requests: Vec) { let mut server = hyper::server::conn::Http::new(); server.http1_only(true); let mut client = ClientBuilder::new(); @@ -295,29 +304,29 @@ pub mod fuzz_logic { let server = build_fuzz_server(cfg, rt, profiles, connect).new_service(accept); let (mut client, bg) = http_util::connect_and_accept(&mut client, server).await; - let http_method = if is_get { - http::Method::GET - } else { - http::Method::POST - }; - - if let Ok(req) = Request::builder() - .method(http_method) - .uri(uri) - .header(header_name, header_value) - .body(Body::default()) - { - let rsp = http_util::http_request(&mut client, req).await; - let _body = http_util::body_to_string(rsp.into_body()).await; + // Now send all of the requests + for inp in requests.iter() { + if let Ok(uri) = std::str::from_utf8(&inp.uri[..]) { + if let Ok(header_name) = std::str::from_utf8(&inp.header_name[..]) { + if let Ok(header_value) = std::str::from_utf8(&inp.header_value[..]) { + let http_method = if inp.http_method { + http::Method::GET + } else { + http::Method::POST + }; - // Let's send one with a correct URL - let req2 = Request::builder() - .method(http::Method::GET) - .uri("http://foo.svc.cluster.local:5550") - .body(Body::default()) - .unwrap(); - let rsp2 = http_util::http_request(&mut client, req2).await; - let _body = http_util::body_to_string(rsp2.into_body()).await; + if let Ok(req) = Request::builder() + .method(http_method) + .uri(uri) + .header(header_name, header_value) + .body(Body::default()) + { + let rsp = http_util::http_request(&mut client, req).await; + let _body = http_util::body_to_string(rsp.into_body()).await; + } + } + } + } } drop(client); diff --git a/linkerd/transport-header/Cargo.toml b/linkerd/transport-header/Cargo.toml index de5d3b64cd..72edff7be7 100644 --- a/linkerd/transport-header/Cargo.toml +++ b/linkerd/transport-header/Cargo.toml @@ -21,6 +21,10 @@ tracing = "0.1.23" [build-dependencies] prost-build = { version = "0.7", default-features = false } +[target.'cfg(fuzzing)'.dependencies] +arbitrary = { version = "1", features = ["derive"] } +libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] } + [dev-dependencies] tokio = { version = "1", features = ["macros"] } tokio-test = "0.4" diff --git a/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_raw.rs b/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_raw.rs index 0b5351044f..326cf23f5b 100644 --- a/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_raw.rs +++ b/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_raw.rs @@ -1,7 +1,8 @@ #![no_main] use libfuzzer_sys::fuzz_target; +use linkerd_transport_header::fuzz_logic::*; fuzz_target!(|data: &[u8]| { let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(linkerd_transport_header::fuzz_logic::fuzz_entry_raw(data)); + rt.block_on(fuzz_entry_raw(data)); }); diff --git a/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_structured.rs b/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_structured.rs index 4469f78978..ac355c9726 100644 --- a/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_structured.rs +++ b/linkerd/transport-header/fuzz/fuzz_targets/fuzz_target_structured.rs @@ -1,22 +1,12 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use libfuzzer_sys::arbitrary::Arbitrary; +use linkerd_transport_header::fuzz_logic::*; -#[derive(Debug, Arbitrary)] -struct TransportHeaderSpec { - data: Vec, - port: u16, - protocol: bool, -} - -fuzz_target!(|inp: TransportHeaderSpec| { - if let Ok(s) = std::str::from_utf8(&inp.data[..]) { +fuzz_target!( + |inp: TransportHeaderSpec| { let rt = tokio::runtime::Runtime::new().unwrap(); - let proto = if inp.protocol { - linkerd_transport_header::SessionProtocol::Http2 - } else { - linkerd_transport_header::SessionProtocol::Http1 - }; - rt.block_on(linkerd_transport_header::fuzz_logic::fuzz_entry_structured(s, inp.port, proto)); + rt.block_on(fuzz_entry_structured( + inp, + )); } -}); +); diff --git a/linkerd/transport-header/src/lib.rs b/linkerd/transport-header/src/lib.rs index 88c73898b1..fc4e79a400 100644 --- a/linkerd/transport-header/src/lib.rs +++ b/linkerd/transport-header/src/lib.rs @@ -273,23 +273,35 @@ mod tests { #[cfg(fuzzing)] pub mod fuzz_logic { use super::*; - pub async fn fuzz_entry_structured( - fuzz_name: &str, - fuzz_port: u16, - fuzz_proto: SessionProtocol, - ) { - let header = TransportHeader { - port: fuzz_port, - name: Name::from_str(fuzz_name).ok(), - protocol: Some(fuzz_proto), - }; - let mut rx = { + use libfuzzer_sys::arbitrary::Arbitrary; + + #[derive(Debug, Arbitrary)] + pub struct TransportHeaderSpec { + data: Vec, + port: u16, + protocol: bool, + } + + pub async fn fuzz_entry_structured(transport_header: TransportHeaderSpec) { + if let Ok(fuzz_name) = std::str::from_utf8(&transport_header.data[..]) { + let fuzz_proto = if transport_header.protocol { + SessionProtocol::Http2 + } else { + SessionProtocol::Http1 + }; + let header = TransportHeader { + port: transport_header.port, + name: Name::from_str(fuzz_name).ok(), + protocol: Some(fuzz_proto), + }; + let mut rx = { + let mut buf = BytesMut::new(); + header.encode_prefaced(&mut buf).expect("must encode"); + std::io::Cursor::new(buf.freeze()) + }; let mut buf = BytesMut::new(); - header.encode_prefaced(&mut buf).expect("must encode"); - std::io::Cursor::new(buf.freeze()) - }; - let mut buf = BytesMut::new(); - let _h = TransportHeader::read_prefaced(&mut rx, &mut buf).await; + let _h = TransportHeader::read_prefaced(&mut rx, &mut buf).await; + } } pub async fn fuzz_entry_raw(fuzz_data: &[u8]) {