fuchsia_fuzzctl_fdomain/
corpus.rs1use crate::input::save_input;
6use anyhow::{Context as _, Error, Result};
7use flex_fuchsia_fuzzer as fuzz;
8use futures::TryStreamExt;
9use std::cell::RefCell;
10use std::path::Path;
11use zx_status as zx;
12
13pub fn get_type(seed: bool) -> fuzz::Corpus {
17 if seed { fuzz::Corpus::Seed } else { fuzz::Corpus::Live }
18}
19
20pub fn get_name(corpus_type: fuzz::Corpus) -> &'static str {
22 match corpus_type {
23 fuzz::Corpus::Seed => "seed",
24 fuzz::Corpus::Live => "live",
25 other => unreachable!("unsupported type: {:?}", other),
26 }
27}
28
29#[derive(Debug, Default, PartialEq)]
31pub struct Stats {
32 pub num_inputs: u64,
33 pub total_size: u64,
34}
35
36pub async fn read<P: AsRef<Path>>(
43 stream: fuzz::CorpusReaderRequestStream,
44 out_dir: P,
45) -> Result<Stats> {
46 let num_inputs: RefCell<u64> = RefCell::new(0);
49 let total_size: RefCell<u64> = RefCell::new(0);
50 stream
51 .try_for_each(|request| async {
52 match request {
53 fuzz::CorpusReaderRequest::Next { test_input, responder } => {
54 {
55 let mut num_inputs = num_inputs.borrow_mut();
56 let mut total_size = total_size.borrow_mut();
57 *num_inputs += 1;
58 *total_size += test_input.size;
59 }
60 let result = match save_input(test_input, out_dir.as_ref()).await {
61 Ok(_) => zx::Status::OK,
62 Err(_) => zx::Status::IO,
63 };
64 responder.send(result.into_raw())
65 }
66 }
67 })
68 .await
69 .map_err(Error::msg)
70 .context("failed to handle fuchsia.fuzzer.CorpusReader request")?;
71 let num_inputs = num_inputs.borrow();
72 let total_size = total_size.borrow();
73 Ok(Stats { num_inputs: *num_inputs, total_size: *total_size })
74}
75
76#[cfg(test)]
77mod tests {
78 use super::{Stats, get_name, get_type, read};
79 use crate::input::InputPair;
80 use crate::util::digest_path;
81 use anyhow::{Error, Result};
82 use flex_fuchsia_fuzzer as fuzz;
83 use fuchsia_fuzzctl_test::{Test, verify_saved};
84 use futures::join;
85 use zx_status as zx;
86
87 async fn send_one_input(
89 corpus_reader: &fuzz::CorpusReaderProxy,
90 data: Vec<u8>,
91 client: &flex_client::ClientArg,
92 ) -> Result<()> {
93 let input_pair = InputPair::try_from_data(client, data)?;
94 let (fidl_input, input) = input_pair.as_tuple();
95 let (response, _) = futures::try_join!(
96 async move { corpus_reader.next(fidl_input).await.map_err(Error::msg) },
97 input.send(),
98 )?;
99 zx::Status::ok(response).map_err(Error::msg)
100 }
101
102 #[test]
103 fn test_get_type() -> Result<()> {
104 assert_eq!(get_type(true), fuzz::Corpus::Seed);
105 assert_eq!(get_type(false), fuzz::Corpus::Live);
106 Ok(())
107 }
108
109 #[test]
110 fn test_get_name() -> Result<()> {
111 assert_eq!(get_name(fuzz::Corpus::Seed), "seed");
112 assert_eq!(get_name(fuzz::Corpus::Live), "live");
113 Ok(())
114 }
115
116 #[fuchsia::test]
117 async fn test_read() -> Result<()> {
118 let test = Test::try_new()?;
119 let corpus_dir = test.create_dir("corpus")?;
120 let corpus = vec![b"hello".to_vec(), b"world".to_vec(), b"".to_vec()];
121 let cloned = corpus.clone();
122
123 let (proxy, stream) = test.domain().create_proxy_and_stream::<fuzz::CorpusReaderMarker>();
124 let read_fut = read(stream, &corpus_dir);
125 let test_ref = &test;
126 let send_fut = || async move {
127 for input in corpus.iter() {
128 send_one_input(&proxy, input.to_vec(), &test_ref.domain()).await?;
129 }
130 Ok::<(), Error>(())
131 };
132 let send_fut = send_fut();
133 let results = join!(read_fut, send_fut);
134 assert_eq!(results.0.unwrap(), Stats { num_inputs: 3, total_size: 10 });
135 assert!(results.1.is_ok());
136 for input in cloned.iter() {
137 let saved = digest_path(&corpus_dir, None, input);
138 verify_saved(&saved, input)?;
139 }
140 Ok(())
141 }
142}