wasmtime_fuzzing/oracles/
engine.rs1use crate::generators::{CompilerStrategy, Config, DiffValue, DiffValueType};
4use crate::oracles::{diff_wasmi::WasmiEngine, diff_wasmtime::WasmtimeEngine};
5use arbitrary::Unstructured;
6use wasmtime::Error;
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" if CompilerStrategy::CRANELIFT_NATIVE_SUPPORTED => Box::new(
20 WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftNative)?,
21 ),
22 "wasmtime" => return Ok(None),
23 "pulley" => Box::new(WasmtimeEngine::new(
24 u,
25 config,
26 CompilerStrategy::CraneliftPulley,
27 )?),
28 "wasmi" => Box::new(WasmiEngine::new(config)),
29
30 "winch" if CompilerStrategy::WINCH_SUPPORTED => {
31 Box::new(WasmtimeEngine::new(u, config, CompilerStrategy::Winch)?)
32 }
33 "winch" => return Ok(None),
34
35 #[cfg(v8)]
36 "v8" => Box::new(crate::oracles::diff_v8::V8Engine::new(config)),
37 #[cfg(not(v8))]
38 "v8" => return Ok(None),
39
40 _ => panic!("unknown engine {name}"),
41 };
42
43 Ok(Some(engine))
44}
45
46pub trait DiffEngine {
48 fn name(&self) -> &'static str;
50
51 fn instantiate(&mut self, wasm: &[u8]) -> wasmtime::Result<Box<dyn DiffInstance>>;
53
54 fn assert_error_match(&self, err: &Error, trap: &Trap);
57
58 fn is_non_deterministic_error(&self, err: &Error) -> bool;
64}
65
66pub trait DiffInstance {
69 fn name(&self) -> &'static str;
71
72 fn evaluate(
78 &mut self,
79 function_name: &str,
80 arguments: &[DiffValue],
81 results: &[DiffValueType],
82 ) -> wasmtime::Result<Option<Vec<DiffValue>>>;
83
84 fn get_global(&mut self, name: &str, ty: DiffValueType) -> Option<DiffValue>;
87
88 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>>;
90}
91
92pub fn setup_engine_runtimes() {
95 }
97
98pub fn build_allowed_env_list<'a>(
125 env_list: Option<Vec<String>>,
126 defaults: &[&'a str],
127) -> Vec<Option<&'a str>> {
128 if let Some(configured) = &env_list {
129 let subtract_from_defaults = configured.iter().all(|c| c.starts_with("-"));
131 let add_from_defaults = configured.iter().all(|c| !c.starts_with("-"));
132 let start = if subtract_from_defaults { 1 } else { 0 };
133 if !subtract_from_defaults && !add_from_defaults {
134 panic!(
135 "all configured values must either subtract or add from defaults; found mixed values: {:?}",
136 &env_list
137 );
138 }
139
140 for c in configured {
142 if !defaults.contains(&&c[start..]) {
143 panic!("invalid environment configuration `{c}`; must be one of: {defaults:?}");
144 }
145 }
146
147 let mut allowed = Vec::with_capacity(defaults.len());
149 for &d in defaults {
150 let mentioned = configured.iter().any(|c| &c[start..] == d);
151 if (add_from_defaults && mentioned) || (subtract_from_defaults && !mentioned) {
152 allowed.push(Some(d));
153 } else {
154 allowed.push(None);
155 }
156 }
157 allowed
158 } else {
159 defaults.iter().copied().map(Some).collect()
160 }
161}
162
163pub fn parse_env_list(env_variable: &str) -> Option<Vec<String>> {
165 std::env::var(env_variable)
166 .ok()
167 .map(|l| l.split(",").map(|s| s.to_owned()).collect())
168}
169
170#[cfg(test)]
172pub fn smoke_test_engine<T>(
173 mk_engine: impl Fn(&mut arbitrary::Unstructured<'_>, &mut Config) -> arbitrary::Result<T>,
174) where
175 T: DiffEngine,
176{
177 crate::test::test_n_times(5, |mut config: Config, u| {
178 config.set_differential_config();
181
182 let mut engine = match mk_engine(u, &mut config) {
183 Ok(engine) => engine,
184 Err(e) => {
185 println!("skip {e:?}");
186 return Ok(());
187 }
188 };
189
190 let wasm = wat::parse_str(
191 r#"
192 (module
193 (func (export "add") (param i32 i32) (result i32)
194 local.get 0
195 local.get 1
196 i32.add)
197
198 (global (export "global") i32 i32.const 1)
199 (memory (export "memory") 1)
200 )
201 "#,
202 )
203 .unwrap();
204 let mut instance = engine.instantiate(&wasm).unwrap();
205 let results = instance
206 .evaluate(
207 "add",
208 &[DiffValue::I32(1), DiffValue::I32(2)],
209 &[DiffValueType::I32],
210 )
211 .unwrap();
212 assert_eq!(results, Some(vec![DiffValue::I32(3)]));
213
214 if let Some(val) = instance.get_global("global", DiffValueType::I32) {
215 assert_eq!(val, DiffValue::I32(1));
216 }
217
218 if let Some(val) = instance.get_memory("memory", false) {
219 assert_eq!(val.len(), 65536);
220 for i in val.iter() {
221 assert_eq!(*i, 0);
222 }
223 }
224
225 Ok(())
226 })
227}