xref: /wasmtime-44.0.1/fuzz/build.rs (revision 72e909c4)

main() -> wasmtime::Result<()>1 fn main() -> wasmtime::Result<()> {
2     component::generate_static_api_tests()?;
3 
4     Ok(())
5 }
6 
7 mod component {
8     use arbitrary::Unstructured;
9     use proc_macro2::TokenStream;
10     use quote::quote;
11     use rand::rngs::StdRng;
12     use rand::{Rng, SeedableRng};
13     use std::collections::HashMap;
14     use std::env;
15     use std::fmt::Write;
16     use std::fs;
17     use std::iter;
18     use std::path::PathBuf;
19     use std::process::Command;
20     use wasmtime::{Error, Result, error::Context as _, format_err};
21     use wasmtime_test_util::component_fuzz::{Declarations, MAX_TYPE_DEPTH, TestCase, Type};
22 
generate_static_api_tests() -> Result<()>23     pub fn generate_static_api_tests() -> Result<()> {
24         println!("cargo:rerun-if-changed=build.rs");
25         let out_dir = PathBuf::from(
26             env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
27         );
28 
29         let mut out = String::new();
30         write_static_api_tests(&mut out)?;
31 
32         let output = out_dir.join("static_component_api.rs");
33         fs::write(&output, out)?;
34 
35         drop(Command::new("rustfmt").arg(&output).status());
36 
37         Ok(())
38     }
39 
write_static_api_tests(out: &mut String) -> Result<()>40     fn write_static_api_tests(out: &mut String) -> Result<()> {
41         println!("cargo:rerun-if-env-changed=WASMTIME_FUZZ_SEED");
42         let seed = if let Ok(seed) = env::var("WASMTIME_FUZZ_SEED") {
43             seed.parse::<u64>()
44                 .with_context(|| format_err!("expected u64 in WASMTIME_FUZZ_SEED"))?
45         } else {
46             StdRng::from_entropy().r#gen()
47         };
48 
49         eprintln!(
50             "using seed {seed} (set WASMTIME_FUZZ_SEED={seed} in your environment to reproduce)"
51         );
52 
53         let mut rng = StdRng::seed_from_u64(seed);
54 
55         const TYPE_COUNT: usize = 50;
56         const TEST_CASE_COUNT: usize = 100;
57 
58         let mut type_fuel = 1000;
59         let mut types = Vec::new();
60         let mut rust_type_names = Vec::new();
61         let name_counter = &mut 0;
62         let mut declarations = TokenStream::new();
63         let mut tests = TokenStream::new();
64 
65         // First generate a set of type to select from.
66         for _ in 0..TYPE_COUNT {
67             let ty = generate(&mut rng, |u| {
68                 // Only discount fuel if the generation was successful,
69                 // otherwise we'll get more random data and try again.
70                 let mut fuel = type_fuel;
71                 let ret = Type::generate(u, MAX_TYPE_DEPTH, &mut fuel);
72                 if ret.is_ok() {
73                     type_fuel = fuel;
74                 }
75                 ret
76             })?;
77 
78             let rust_ty_name =
79                 wasmtime_test_util::component_fuzz::rust_type(&ty, name_counter, &mut declarations);
80             types.push(ty);
81             rust_type_names.push(rust_ty_name);
82         }
83 
84         fn hash_key(ty: &Type) -> usize {
85             let ty: *const Type = ty;
86             ty.addr()
87         }
88 
89         let type_to_name_map = types
90             .iter()
91             .map(hash_key)
92             .zip(rust_type_names.iter().cloned())
93             .collect::<HashMap<_, _>>();
94 
95         // Next generate a set of static API test cases driven by the above
96         // types.
97         for index in 0..TEST_CASE_COUNT {
98             let (case, rust_params, rust_results) = generate(&mut rng, |u| {
99                 let mut rust_params = TokenStream::new();
100                 let mut rust_results = TokenStream::new();
101                 let case = TestCase::generate(&types, u)?;
102                 for ty in case.params.iter() {
103                     let name = &type_to_name_map[&hash_key(ty)];
104                     rust_params.extend(name.clone());
105                     rust_params.extend(quote!(,));
106                 }
107                 if let Some(ty) = &case.result {
108                     let name = &type_to_name_map[&hash_key(ty)];
109                     rust_results.extend(name.clone());
110                     rust_results.extend(quote!(,));
111                 }
112                 Ok((case, rust_params, rust_results))
113             })?;
114 
115             let Declarations {
116                 types,
117                 type_instantiation_args,
118                 params,
119                 results,
120                 caller_module,
121                 callee_module,
122                 options,
123             } = case.declarations();
124 
125             let test = quote!(#index => component_api::static_api_test::<(#rust_params), (#rust_results)>(
126                 input,
127                 {
128                     static DECLS: Declarations = Declarations {
129                         types: Cow::Borrowed(#types),
130                         type_instantiation_args: Cow::Borrowed(#type_instantiation_args),
131                         params: Cow::Borrowed(#params),
132                         results: Cow::Borrowed(#results),
133                         caller_module: Cow::Borrowed(#caller_module),
134                         callee_module: Cow::Borrowed(#callee_module),
135                         options: #options,
136                     };
137                     &DECLS
138                 }
139             ),);
140 
141             tests.extend(test);
142         }
143 
144         let module = quote! {
145             #[allow(unused_imports, reason = "macro-generated code")]
146             fn static_component_api_target(input: &mut libfuzzer_sys::arbitrary::Unstructured) -> libfuzzer_sys::arbitrary::Result<()> {
147                 use wasmtime::Result;
148                 use wasmtime_test_util::component_fuzz::Declarations;
149                 use wasmtime_test_util::component::{Float32, Float64};
150                 use libfuzzer_sys::arbitrary::{self, Arbitrary};
151                 use std::borrow::Cow;
152                 use std::sync::{Arc, Once};
153                 use wasmtime::component::{ComponentType, Lift, Lower};
154                 use wasmtime_fuzzing::oracles::component_api;
155 
156                 const SEED: u64 = #seed;
157 
158                 static ONCE: Once = Once::new();
159 
160                 ONCE.call_once(|| {
161                     eprintln!(
162                         "Seed {SEED} was used to generate static component API fuzz tests.\n\
163                          Set WASMTIME_FUZZ_SEED={SEED} in your environment at build time to reproduce."
164                     );
165                 });
166 
167                 #declarations
168 
169                 match input.int_in_range(0..=(#TEST_CASE_COUNT-1))? {
170                     #tests
171                     _ => unreachable!()
172                 }
173             }
174         };
175 
176         write!(out, "{module}")?;
177 
178         Ok(())
179     }
180 
generate<T>( rng: &mut StdRng, mut f: impl FnMut(&mut Unstructured<'_>) -> arbitrary::Result<T>, ) -> Result<T>181     fn generate<T>(
182         rng: &mut StdRng,
183         mut f: impl FnMut(&mut Unstructured<'_>) -> arbitrary::Result<T>,
184     ) -> Result<T> {
185         let mut bytes = Vec::new();
186         loop {
187             let count = rng.gen_range(1000..2000);
188             bytes.extend(iter::repeat_with(|| rng.r#gen::<u8>()).take(count));
189 
190             match f(&mut Unstructured::new(&bytes)) {
191                 Ok(ret) => break Ok(ret),
192                 Err(arbitrary::Error::NotEnoughData) => (),
193                 Err(error) => break Err(Error::from(error)),
194             }
195         }
196     }
197 }
198