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