1 //! Test case generators.
2 //!
3 //! Test case generators take raw, unstructured input from a fuzzer
4 //! (e.g. libFuzzer) and translate that into a structured test case (e.g. a
5 //! valid Wasm binary).
6 //!
7 //! These are generally implementations of the `Arbitrary` trait, or some
8 //! wrapper over an external tool, such that the wrapper implements the
9 //! `Arbitrary` trait for the wrapped external tool.
10 
11 #[cfg(feature = "binaryen")]
12 pub mod api;
13 
14 use arbitrary::Arbitrary;
15 
16 /// A Wasm test case generator that is powered by Binaryen's `wasm-opt -ttf`.
17 #[derive(Clone)]
18 #[cfg(feature = "binaryen")]
19 pub struct WasmOptTtf {
20     /// The raw, encoded Wasm bytes.
21     pub wasm: Vec<u8>,
22 }
23 
24 #[cfg(feature = "binaryen")]
25 impl std::fmt::Debug for WasmOptTtf {
26     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
27         write!(
28             f,
29             "WasmOptTtf {{ wasm: wat::parse_str(r###\"\n{}\n\"###).unwrap() }}",
30             wasmprinter::print_bytes(&self.wasm).expect("valid wasm should always disassemble")
31         )
32     }
33 }
34 
35 #[cfg(feature = "binaryen")]
36 impl Arbitrary for WasmOptTtf {
37     fn arbitrary(input: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
38         crate::init_fuzzing();
39         let seed: Vec<u8> = Arbitrary::arbitrary(input)?;
40         let module = binaryen::tools::translate_to_fuzz_mvp(&seed);
41         let wasm = module.write();
42         Ok(WasmOptTtf { wasm })
43     }
44 
45     fn arbitrary_take_rest(input: arbitrary::Unstructured) -> arbitrary::Result<Self> {
46         crate::init_fuzzing();
47         let seed: Vec<u8> = Arbitrary::arbitrary_take_rest(input)?;
48         let module = binaryen::tools::translate_to_fuzz_mvp(&seed);
49         let wasm = module.write();
50         Ok(WasmOptTtf { wasm })
51     }
52 
53     fn size_hint(depth: usize) -> (usize, Option<usize>) {
54         <Vec<u8> as Arbitrary>::size_hint(depth)
55     }
56 }
57 
58 /// A description of configuration options that we should do differential
59 /// testing between.
60 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
61 pub struct DifferentialConfig {
62     strategy: DifferentialStrategy,
63     opt_level: DifferentialOptLevel,
64 }
65 
66 impl DifferentialConfig {
67     /// Convert this differential fuzzing config into a `wasmtime::Config`.
68     pub fn to_wasmtime_config(&self) -> anyhow::Result<wasmtime::Config> {
69         let mut config = wasmtime::Config::new();
70         config.cranelift_debug_verifier(true);
71         config.strategy(match self.strategy {
72             DifferentialStrategy::Cranelift => wasmtime::Strategy::Cranelift,
73             DifferentialStrategy::Lightbeam => wasmtime::Strategy::Lightbeam,
74         })?;
75         config.cranelift_opt_level(match self.opt_level {
76             DifferentialOptLevel::None => wasmtime::OptLevel::None,
77             DifferentialOptLevel::Speed => wasmtime::OptLevel::Speed,
78             DifferentialOptLevel::SpeedAndSize => wasmtime::OptLevel::SpeedAndSize,
79         });
80         Ok(config)
81     }
82 }
83 
84 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
85 enum DifferentialStrategy {
86     Cranelift,
87     Lightbeam,
88 }
89 
90 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
91 enum DifferentialOptLevel {
92     None,
93     Speed,
94     SpeedAndSize,
95 }
96