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 pub mod api;
12 
13 pub mod table_ops;
14 
15 use arbitrary::{Arbitrary, Unstructured};
16 
17 /// A description of configuration options that we should do differential
18 /// testing between.
19 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
20 pub struct DifferentialConfig {
21     strategy: DifferentialStrategy,
22     opt_level: OptLevel,
23 }
24 
25 impl DifferentialConfig {
26     /// Convert this differential fuzzing config into a `wasmtime::Config`.
27     pub fn to_wasmtime_config(&self) -> anyhow::Result<wasmtime::Config> {
28         let mut config = crate::fuzz_default_config(match self.strategy {
29             DifferentialStrategy::Cranelift => wasmtime::Strategy::Cranelift,
30             DifferentialStrategy::Lightbeam => wasmtime::Strategy::Lightbeam,
31         })?;
32         config.cranelift_opt_level(self.opt_level.to_wasmtime());
33         Ok(config)
34     }
35 }
36 
37 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
38 enum DifferentialStrategy {
39     Cranelift,
40     Lightbeam,
41 }
42 
43 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq, Hash)]
44 enum OptLevel {
45     None,
46     Speed,
47     SpeedAndSize,
48 }
49 
50 impl OptLevel {
51     fn to_wasmtime(&self) -> wasmtime::OptLevel {
52         match self {
53             OptLevel::None => wasmtime::OptLevel::None,
54             OptLevel::Speed => wasmtime::OptLevel::Speed,
55             OptLevel::SpeedAndSize => wasmtime::OptLevel::SpeedAndSize,
56         }
57     }
58 }
59 
60 /// Implementation of generating a `wasmtime::Config` arbitrarily
61 #[derive(Arbitrary, Debug)]
62 pub struct Config {
63     opt_level: OptLevel,
64     debug_info: bool,
65     canonicalize_nans: bool,
66     interruptable: bool,
67     #[allow(missing_docs)]
68     pub consume_fuel: bool,
69 
70     // Note that we use 32-bit values here to avoid blowing the 64-bit address
71     // space by requesting ungodly-large sizes/guards.
72     static_memory_maximum_size: Option<u32>,
73     static_memory_guard_size: Option<u32>,
74     dynamic_memory_guard_size: Option<u32>,
75     guard_before_linear_memory: bool,
76 }
77 
78 impl Config {
79     /// Converts this to a `wasmtime::Config` object
80     pub fn to_wasmtime(&self) -> wasmtime::Config {
81         let mut cfg = crate::fuzz_default_config(wasmtime::Strategy::Auto).unwrap();
82         cfg.debug_info(self.debug_info)
83             .static_memory_maximum_size(self.static_memory_maximum_size.unwrap_or(0).into())
84             .static_memory_guard_size(self.static_memory_guard_size.unwrap_or(0).into())
85             .dynamic_memory_guard_size(self.dynamic_memory_guard_size.unwrap_or(0).into())
86             .guard_before_linear_memory(self.guard_before_linear_memory)
87             .cranelift_nan_canonicalization(self.canonicalize_nans)
88             .cranelift_opt_level(self.opt_level.to_wasmtime())
89             .interruptable(self.interruptable)
90             .consume_fuel(self.consume_fuel);
91         return cfg;
92     }
93 }
94 
95 include!(concat!(env!("OUT_DIR"), "/spectests.rs"));
96 
97 /// A spec test from the upstream wast testsuite, arbitrarily chosen from the
98 /// list of known spec tests.
99 #[derive(Debug)]
100 pub struct SpecTest {
101     /// The filename of the spec test
102     pub file: &'static str,
103     /// The `*.wast` contents of the spec test
104     pub contents: &'static str,
105 }
106 
107 impl<'a> Arbitrary<'a> for SpecTest {
108     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
109         // NB: this does get a uniform value in the provided range.
110         let i = u.int_in_range(0..=FILES.len() - 1)?;
111         let (file, contents) = FILES[i];
112         Ok(SpecTest { file, contents })
113     }
114 
115     fn size_hint(_depth: usize) -> (usize, Option<usize>) {
116         (1, Some(std::mem::size_of::<usize>()))
117     }
118 }
119 
120 /// Type alias for wasm-smith generated modules using wasmtime's default
121 /// configuration.
122 pub type GeneratedModule = wasm_smith::ConfiguredModule<WasmtimeDefaultConfig>;
123 
124 /// Wasmtime-specific default configuration for wasm-smith-generated modules.
125 #[derive(Arbitrary, Clone, Debug)]
126 pub struct WasmtimeDefaultConfig;
127 
128 impl wasm_smith::Config for WasmtimeDefaultConfig {
129     // Allow multi-memory to get exercised
130     fn max_memories(&self) -> usize {
131         2
132     }
133 
134     // Allow multi-table (reference types) to get exercised
135     fn max_tables(&self) -> usize {
136         4
137     }
138 
139     // Turn some wasm features default-on for those that have a finished
140     // implementation in Wasmtime.
141     fn simd_enabled(&self) -> bool {
142         true
143     }
144 
145     fn reference_types_enabled(&self) -> bool {
146         true
147     }
148 
149     fn bulk_memory_enabled(&self) -> bool {
150         true
151     }
152 
153     fn memory64_enabled(&self) -> bool {
154         true
155     }
156 }
157