wasmtime_fuzzing/oracles/
engine.rs1use crate::generators::{CompilerStrategy, Config, DiffValue, DiffValueType};
4use crate::oracles::{diff_wasmi::WasmiEngine, diff_wasmtime::WasmtimeEngine};
5use anyhow::Error;
6use arbitrary::Unstructured;
7use wasmtime::Trap;
8
9pub fn build(
14 u: &mut Unstructured<'_>,
15 name: &str,
16 config: &mut Config,
17) -> arbitrary::Result<Option<Box<dyn DiffEngine>>> {
18 let engine: Box<dyn DiffEngine> = match name {
19 "wasmtime" => Box::new(WasmtimeEngine::new(
20 u,
21 config,
22 CompilerStrategy::CraneliftNative,
23 )?),
24 "pulley" => Box::new(WasmtimeEngine::new(
25 u,
26 config,
27 CompilerStrategy::CraneliftPulley,
28 )?),
29 "wasmi" => Box::new(WasmiEngine::new(config)),
30
31 #[cfg(target_arch = "x86_64")]
32 "winch" => Box::new(WasmtimeEngine::new(u, config, CompilerStrategy::Winch)?),
33 #[cfg(not(target_arch = "x86_64"))]
34 "winch" => return Ok(None),
35
36 #[cfg(feature = "fuzz-spec-interpreter")]
37 "spec" => Box::new(crate::oracles::diff_spec::SpecInterpreter::new(config)),
38 #[cfg(not(feature = "fuzz-spec-interpreter"))]
39 "spec" => return Ok(None),
40
41 #[cfg(not(any(windows, target_arch = "s390x", target_arch = "riscv64")))]
42 "v8" => Box::new(crate::oracles::diff_v8::V8Engine::new(config)),
43 #[cfg(any(windows, target_arch = "s390x", target_arch = "riscv64"))]
44 "v8" => return Ok(None),
45
46 _ => panic!("unknown engine {name}"),
47 };
48
49 Ok(Some(engine))
50}
51
52pub trait DiffEngine {
54 fn name(&self) -> &'static str;
56
57 fn instantiate(&mut self, wasm: &[u8]) -> anyhow::Result<Box<dyn DiffInstance>>;
59
60 fn assert_error_match(&self, err: &Error, trap: &Trap);
63
64 fn is_non_deterministic_error(&self, err: &Error) -> bool;
70}
71
72pub trait DiffInstance {
75 fn name(&self) -> &'static str;
77
78 fn evaluate(
84 &mut self,
85 function_name: &str,
86 arguments: &[DiffValue],
87 results: &[DiffValueType],
88 ) -> anyhow::Result<Option<Vec<DiffValue>>>;
89
90 fn get_global(&mut self, name: &str, ty: DiffValueType) -> Option<DiffValue>;
93
94 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>>;
96}
97
98pub fn setup_engine_runtimes() {
101 #[cfg(feature = "fuzz-spec-interpreter")]
102 crate::oracles::diff_spec::setup_ocaml_runtime();
103}
104
105pub fn build_allowed_env_list<'a>(
132 env_list: Option<Vec<String>>,
133 defaults: &[&'a str],
134) -> Vec<Option<&'a str>> {
135 if let Some(configured) = &env_list {
136 let subtract_from_defaults = configured.iter().all(|c| c.starts_with("-"));
138 let add_from_defaults = configured.iter().all(|c| !c.starts_with("-"));
139 let start = if subtract_from_defaults { 1 } else { 0 };
140 if !subtract_from_defaults && !add_from_defaults {
141 panic!(
142 "all configured values must either subtract or add from defaults; found mixed values: {:?}",
143 &env_list
144 );
145 }
146
147 for c in configured {
149 if !defaults.contains(&&c[start..]) {
150 panic!("invalid environment configuration `{c}`; must be one of: {defaults:?}");
151 }
152 }
153
154 let mut allowed = Vec::with_capacity(defaults.len());
156 for &d in defaults {
157 let mentioned = configured.iter().any(|c| &c[start..] == d);
158 if (add_from_defaults && mentioned) || (subtract_from_defaults && !mentioned) {
159 allowed.push(Some(d));
160 } else {
161 allowed.push(None);
162 }
163 }
164 allowed
165 } else {
166 defaults.iter().copied().map(Some).collect()
167 }
168}
169
170pub fn parse_env_list(env_variable: &str) -> Option<Vec<String>> {
172 std::env::var(env_variable)
173 .ok()
174 .map(|l| l.split(",").map(|s| s.to_owned()).collect())
175}
176
177#[cfg(test)]
179pub fn smoke_test_engine<T>(
180 mk_engine: impl Fn(&mut arbitrary::Unstructured<'_>, &mut Config) -> arbitrary::Result<T>,
181) where
182 T: DiffEngine,
183{
184 use rand::prelude::*;
185
186 let mut rng = SmallRng::seed_from_u64(0);
187 let mut buf = vec![0; 2048];
188 let n = 100;
189 for _ in 0..n {
190 rng.fill_bytes(&mut buf);
191 let mut u = Unstructured::new(&buf);
192 let mut config = match u.arbitrary::<Config>() {
193 Ok(config) => config,
194 Err(_) => continue,
195 };
196 config.set_differential_config();
199
200 let mut engine = match mk_engine(&mut u, &mut config) {
201 Ok(engine) => engine,
202 Err(e) => {
203 println!("skip {e:?}");
204 continue;
205 }
206 };
207
208 let wasm = wat::parse_str(
209 r#"
210 (module
211 (func (export "add") (param i32 i32) (result i32)
212 local.get 0
213 local.get 1
214 i32.add)
215
216 (global (export "global") i32 i32.const 1)
217 (memory (export "memory") 1)
218 )
219 "#,
220 )
221 .unwrap();
222 let mut instance = engine.instantiate(&wasm).unwrap();
223 let results = instance
224 .evaluate(
225 "add",
226 &[DiffValue::I32(1), DiffValue::I32(2)],
227 &[DiffValueType::I32],
228 )
229 .unwrap();
230 assert_eq!(results, Some(vec![DiffValue::I32(3)]));
231
232 if let Some(val) = instance.get_global("global", DiffValueType::I32) {
233 assert_eq!(val, DiffValue::I32(1));
234 }
235
236 if let Some(val) = instance.get_memory("memory", false) {
237 assert_eq!(val.len(), 65536);
238 for i in val.iter() {
239 assert_eq!(*i, 0);
240 }
241 }
242
243 return;
244 }
245
246 panic!("after {n} runs nothing ever ran, something is probably wrong");
247}