Skip to main content

wasmtime_fuzzing/oracles/
diff_wasmtime.rs

1//! Evaluate an exported Wasm function using Wasmtime.
2
3use crate::generators::{self, CompilerStrategy, DiffValue, DiffValueType, WasmtimeConfig};
4use crate::oracles::dummy;
5use crate::oracles::engine::DiffInstance;
6use crate::oracles::{StoreLimits, compile_module, engine::DiffEngine};
7use crate::single_module_fuzzer::KnownValid;
8use arbitrary::Unstructured;
9use wasmtime::{
10    Error, Extern, FuncType, Instance, Module, Result, Store, Trap, Val, error::Context as _,
11};
12
13/// A wrapper for using Wasmtime as a [`DiffEngine`].
14pub struct WasmtimeEngine {
15    config: generators::Config,
16}
17
18impl WasmtimeEngine {
19    /// Merely store the configuration; the engine is actually constructed
20    /// later. Ideally the store and engine could be built here but
21    /// `compile_module` takes a [`generators::Config`]; TODO re-factor this if
22    /// that ever changes.
23    pub fn new(
24        u: &mut Unstructured<'_>,
25        config: &mut generators::Config,
26        compiler_strategy: CompilerStrategy,
27    ) -> arbitrary::Result<Self> {
28        let mut new_config = u.arbitrary::<WasmtimeConfig>()?;
29        new_config.compiler_strategy = compiler_strategy;
30        new_config.update_module_config(&mut config.module_config, u)?;
31        new_config.make_compatible_with(&config.wasmtime);
32
33        let config = generators::Config {
34            wasmtime: new_config,
35            module_config: config.module_config.clone(),
36        };
37        log::debug!("Created new Wasmtime differential engine with config: {config:?}");
38
39        Ok(Self { config })
40    }
41}
42
43impl DiffEngine for WasmtimeEngine {
44    fn name(&self) -> &'static str {
45        match self.config.wasmtime.compiler_strategy {
46            CompilerStrategy::CraneliftNative => "wasmtime",
47            CompilerStrategy::Winch => "winch",
48            CompilerStrategy::CraneliftPulley => "pulley",
49        }
50    }
51
52    fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
53        let store = self.config.to_store();
54        let module = compile_module(store.engine(), wasm, KnownValid::Yes, &self.config).unwrap();
55        let instance = WasmtimeInstance::new(store, module)?;
56        Ok(Box::new(instance))
57    }
58
59    fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {
60        let lhs = lhs
61            .downcast_ref::<Trap>()
62            .expect(&format!("not a trap: {lhs:?}"));
63
64        assert_eq!(lhs, rhs, "{lhs}\nis not equal to\n{rhs}");
65    }
66
67    fn is_non_deterministic_error(&self, err: &Error) -> bool {
68        super::wasmtime_error_is_non_deterministic(err)
69    }
70}
71
72/// A wrapper around a Wasmtime instance.
73///
74/// The Wasmtime engine constructs a new store and compiles an instance of a
75/// Wasm module.
76pub struct WasmtimeInstance {
77    store: Store<StoreLimits>,
78    instance: Instance,
79}
80
81impl WasmtimeInstance {
82    /// Instantiate a new Wasmtime instance.
83    pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> {
84        let instance = dummy::dummy_linker(&mut store, &module)
85            .and_then(|l| l.instantiate(&mut store, &module))
86            .context("unable to instantiate module in wasmtime")?;
87        Ok(Self { store, instance })
88    }
89
90    /// Retrieve the names and types of all exported functions in the instance.
91    ///
92    /// This is useful for evaluating each exported function with different
93    /// values. The [`DiffInstance`] trait asks for the function name and we
94    /// need to know the function signature in order to pass in the right
95    /// arguments.
96    pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> {
97        let exported_functions = self
98            .instance
99            .exports(&mut self.store)
100            .map(|e| (e.name().to_owned(), e.into_func()))
101            .filter_map(|(n, f)| f.map(|f| (n, f)))
102            .collect::<Vec<_>>();
103        exported_functions
104            .into_iter()
105            .map(|(n, f)| (n, f.ty(&self.store)))
106            .collect()
107    }
108
109    /// Returns the list of globals and their types exported from this instance.
110    pub fn exported_globals(&mut self) -> Vec<(String, DiffValueType)> {
111        let globals = self
112            .instance
113            .exports(&mut self.store)
114            .filter_map(|e| {
115                let name = e.name();
116                e.into_global().map(|g| (name.to_string(), g))
117            })
118            .collect::<Vec<_>>();
119
120        globals
121            .into_iter()
122            .filter_map(|(name, global)| {
123                DiffValueType::try_from(global.ty(&self.store).content().clone())
124                    .map(|ty| (name, ty))
125                    .ok()
126            })
127            .collect()
128    }
129
130    /// Returns the list of exported memories and whether or not it's a shared
131    /// memory.
132    pub fn exported_memories(&mut self) -> Vec<(String, bool)> {
133        self.instance
134            .exports(&mut self.store)
135            .filter_map(|e| {
136                let name = e.name();
137                match e.into_extern() {
138                    Extern::Memory(_) => Some((name.to_string(), false)),
139                    Extern::SharedMemory(_) => Some((name.to_string(), true)),
140                    _ => None,
141                }
142            })
143            .collect()
144    }
145
146    /// Returns whether or not this instance has hit its OOM condition yet.
147    pub fn is_oom(&self) -> bool {
148        self.store.data().is_oom()
149    }
150}
151
152impl DiffInstance for WasmtimeInstance {
153    fn name(&self) -> &'static str {
154        "wasmtime"
155    }
156
157    fn evaluate(
158        &mut self,
159        function_name: &str,
160        arguments: &[DiffValue],
161        _results: &[DiffValueType],
162    ) -> Result<Option<Vec<DiffValue>>> {
163        let arguments: Vec<_> = arguments.iter().map(Val::from).collect();
164
165        let function = self
166            .instance
167            .get_func(&mut self.store, function_name)
168            .expect("unable to access exported function");
169        let ty = function.ty(&self.store);
170        let mut results = vec![Val::I32(0); ty.results().len()];
171        function.call(&mut self.store, &arguments, &mut results)?;
172
173        let results = results.into_iter().map(Val::into).collect();
174        Ok(Some(results))
175    }
176
177    fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> {
178        Some(
179            self.instance
180                .get_global(&mut self.store, name)
181                .unwrap()
182                .get(&mut self.store)
183                .into(),
184        )
185    }
186
187    fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> {
188        Some(if shared {
189            let memory = self
190                .instance
191                .get_shared_memory(&mut self.store, name)
192                .unwrap();
193            memory.data().iter().map(|i| unsafe { *i.get() }).collect()
194        } else {
195            self.instance
196                .get_memory(&mut self.store, name)
197                .unwrap()
198                .data(&self.store)
199                .to_vec()
200        })
201    }
202}
203
204impl From<&DiffValue> for Val {
205    fn from(v: &DiffValue) -> Self {
206        match *v {
207            DiffValue::I32(n) => Val::I32(n),
208            DiffValue::I64(n) => Val::I64(n),
209            DiffValue::F32(n) => Val::F32(n),
210            DiffValue::F64(n) => Val::F64(n),
211            DiffValue::V128(n) => Val::V128(n.into()),
212            DiffValue::FuncRef { null } => {
213                assert!(null);
214                Val::FuncRef(None)
215            }
216            DiffValue::ExternRef { null } => {
217                assert!(null);
218                Val::ExternRef(None)
219            }
220            DiffValue::AnyRef { null } => {
221                assert!(null);
222                Val::AnyRef(None)
223            }
224            DiffValue::ExnRef { null } => {
225                assert!(null);
226                Val::ExnRef(None)
227            }
228            DiffValue::ContRef { null } => {
229                assert!(null);
230                Val::ExnRef(None)
231            }
232        }
233    }
234}
235
236impl From<Val> for DiffValue {
237    fn from(val: Val) -> DiffValue {
238        match val {
239            Val::I32(n) => DiffValue::I32(n),
240            Val::I64(n) => DiffValue::I64(n),
241            Val::F32(n) => DiffValue::F32(n),
242            Val::F64(n) => DiffValue::F64(n),
243            Val::V128(n) => DiffValue::V128(n.into()),
244            Val::ExternRef(r) => DiffValue::ExternRef { null: r.is_none() },
245            Val::FuncRef(r) => DiffValue::FuncRef { null: r.is_none() },
246            Val::AnyRef(r) => DiffValue::AnyRef { null: r.is_none() },
247            Val::ExnRef(e) => DiffValue::ExnRef { null: e.is_none() },
248            Val::ContRef(c) => DiffValue::ContRef { null: c.is_none() },
249        }
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[test]
258    fn smoke_cranelift_native() {
259        if !CompilerStrategy::CRANELIFT_NATIVE_SUPPORTED {
260            return;
261        }
262        crate::oracles::engine::smoke_test_engine(|u, config| {
263            WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftNative)
264        })
265    }
266
267    #[test]
268    fn smoke_cranelift_pulley() {
269        crate::oracles::engine::smoke_test_engine(|u, config| {
270            WasmtimeEngine::new(u, config, CompilerStrategy::CraneliftPulley)
271        })
272    }
273
274    #[test]
275    fn smoke_winch() {
276        if !CompilerStrategy::WINCH_SUPPORTED {
277            return;
278        }
279        crate::oracles::engine::smoke_test_engine(|u, config| {
280            WasmtimeEngine::new(u, config, CompilerStrategy::Winch)
281        })
282    }
283}