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