1 //! Oracles.
2 //!
3 //! Oracles take a test case and determine whether we have a bug. For example,
4 //! one of the simplest oracles is to take a Wasm binary as our input test case,
5 //! validate and instantiate it, and (implicitly) check that no assertions
6 //! failed or segfaults happened. A more complicated oracle might compare the
7 //! result of executing a Wasm file with and without optimizations enabled, and
8 //! make sure that the two executions are observably identical.
9 //!
10 //! When an oracle finds a bug, it should report it to the fuzzing engine by
11 //! panicking.
12 
13 pub mod dummy;
14 
15 use crate::generators;
16 use anyhow::Context;
17 use arbitrary::Arbitrary;
18 use log::{debug, warn};
19 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
20 use std::sync::{Arc, Condvar, Mutex};
21 use std::time::{Duration, Instant};
22 use wasmtime::*;
23 use wasmtime_wast::WastContext;
24 
25 #[cfg(not(any(windows, target_arch = "s390x")))]
26 pub use self::v8::*;
27 #[cfg(not(any(windows, target_arch = "s390x")))]
28 mod v8;
29 
30 static CNT: AtomicUsize = AtomicUsize::new(0);
31 
32 /// Logs a wasm file to the filesystem to make it easy to figure out what wasm
33 /// was used when debugging.
34 pub fn log_wasm(wasm: &[u8]) {
35     super::init_fuzzing();
36 
37     if !log::log_enabled!(log::Level::Debug) {
38         return;
39     }
40 
41     let i = CNT.fetch_add(1, SeqCst);
42     let name = format!("testcase{}.wasm", i);
43     std::fs::write(&name, wasm).expect("failed to write wasm file");
44     log::debug!("wrote wasm file to `{}`", name);
45     let wat = format!("testcase{}.wat", i);
46     match wasmprinter::print_bytes(wasm) {
47         Ok(s) => std::fs::write(&wat, s).expect("failed to write wat file"),
48         // If wasmprinter failed remove a `*.wat` file, if any, to avoid
49         // confusing a preexisting one with this wasm which failed to get
50         // printed.
51         Err(_) => drop(std::fs::remove_file(&wat)),
52     }
53 }
54 
55 /// The `T` in `Store<T>` for fuzzing stores, used to limit resource
56 /// consumption during fuzzing.
57 pub struct StoreLimits {
58     /// Remaining memory, in bytes, left to allocate
59     remaining_memory: usize,
60     /// Whether or not an allocation request has been denied
61     oom: bool,
62 }
63 
64 impl StoreLimits {
65     /// Creates the default set of limits for all fuzzing stores.
66     pub fn new() -> StoreLimits {
67         StoreLimits {
68             // Limits tables/memories within a store to at most 1gb for now to
69             // exercise some larger address but not overflow various limits.
70             remaining_memory: 1 << 30,
71             oom: false,
72         }
73     }
74 
75     fn alloc(&mut self, amt: usize) -> bool {
76         match self.remaining_memory.checked_sub(amt) {
77             Some(mem) => {
78                 self.remaining_memory = mem;
79                 true
80             }
81             None => {
82                 self.oom = true;
83                 false
84             }
85         }
86     }
87 }
88 
89 impl ResourceLimiter for StoreLimits {
90     fn memory_growing(&mut self, current: usize, desired: usize, _maximum: Option<usize>) -> bool {
91         self.alloc(desired - current)
92     }
93 
94     fn table_growing(&mut self, current: u32, desired: u32, _maximum: Option<u32>) -> bool {
95         let delta = (desired - current) as usize * std::mem::size_of::<usize>();
96         self.alloc(delta)
97     }
98 }
99 
100 /// Methods of timing out execution of a WebAssembly module
101 #[derive(Debug)]
102 pub enum Timeout {
103     /// No timeout is used, it should be guaranteed via some other means that
104     /// the input does not infinite loop.
105     None,
106     /// A time-based timeout is used with a sleeping thread sending a signal
107     /// after the specified duration.
108     Time(Duration),
109     /// Fuel-based timeouts are used where the specified fuel is all that the
110     /// provided wasm module is allowed to consume.
111     Fuel(u64),
112 }
113 
114 /// Instantiate the Wasm buffer, and implicitly fail if we have an unexpected
115 /// panic or segfault or anything else that can be detected "passively".
116 ///
117 /// The engine will be configured using provided config.
118 pub fn instantiate(wasm: &[u8], known_valid: bool, config: &generators::Config, timeout: Timeout) {
119     let mut store = config.to_store();
120 
121     let mut timeout_state = SignalOnDrop::default();
122     match timeout {
123         Timeout::Fuel(fuel) => {
124             // consume the default fuel in the store ...
125             let remaining = store.consume_fuel(0).unwrap();
126             store.consume_fuel(remaining - 1).unwrap();
127             // ... then add back in how much fuel we're allowing here
128             store.add_fuel(fuel).unwrap();
129         }
130         // If a timeout is requested then we spawn a helper thread to wait for
131         // the requested time and then send us a signal to get interrupted. We
132         // also arrange for the thread's sleep to get interrupted if we return
133         // early (or the wasm returns within the time limit), which allows the
134         // thread to get torn down.
135         //
136         // This prevents us from creating a huge number of sleeping threads if
137         // this function is executed in a loop, like it does on nightly fuzzing
138         // infrastructure.
139         Timeout::Time(timeout) => {
140             let handle = store.interrupt_handle().unwrap();
141             timeout_state.spawn_timeout(timeout, move || handle.interrupt());
142         }
143         Timeout::None => {}
144     }
145 
146     if let Some(module) = compile_module(store.engine(), wasm, known_valid, config) {
147         instantiate_with_dummy(&mut store, &module);
148     }
149 }
150 
151 /// Represents supported commands to the `instantiate_many` function.
152 #[derive(Arbitrary, Debug)]
153 pub enum Command {
154     /// Instantiates a module.
155     ///
156     /// The value is the index of the module to instantiate.
157     ///
158     /// The module instantiated will be this value modulo the number of modules provided to `instantiate_many`.
159     Instantiate(usize),
160     /// Terminates a "running" instance.
161     ///
162     /// The value is the index of the instance to terminate.
163     ///
164     /// The instance terminated will be this value modulo the number of currently running
165     /// instances.
166     ///
167     /// If no instances are running, the command will be ignored.
168     Terminate(usize),
169 }
170 
171 /// Instantiates many instances from the given modules.
172 ///
173 /// The engine will be configured using the provided config.
174 ///
175 /// The modules are expected to *not* have start functions as no timeouts are configured.
176 pub fn instantiate_many(
177     modules: &[Vec<u8>],
178     known_valid: bool,
179     config: &generators::Config,
180     commands: &[Command],
181 ) {
182     assert!(!config.module_config.config.allow_start_export);
183 
184     let engine = Engine::new(&config.to_wasmtime()).unwrap();
185 
186     let modules = modules
187         .iter()
188         .filter_map(|bytes| compile_module(&engine, bytes, known_valid, config))
189         .collect::<Vec<_>>();
190 
191     // If no modules were valid, we're done
192     if modules.is_empty() {
193         return;
194     }
195 
196     // This stores every `Store` where a successful instantiation takes place
197     let mut stores = Vec::new();
198 
199     for command in commands {
200         match command {
201             Command::Instantiate(index) => {
202                 let module = &modules[*index % modules.len()];
203                 let mut store = Store::new(&engine, StoreLimits::new());
204                 config.configure_store(&mut store);
205 
206                 if instantiate_with_dummy(&mut store, module).is_some() {
207                     stores.push(Some(store));
208                 }
209             }
210             Command::Terminate(index) => {
211                 if stores.is_empty() {
212                     continue;
213                 }
214 
215                 stores.swap_remove(*index % stores.len());
216             }
217         }
218     }
219 }
220 
221 fn compile_module(
222     engine: &Engine,
223     bytes: &[u8],
224     known_valid: bool,
225     config: &generators::Config,
226 ) -> Option<Module> {
227     log_wasm(bytes);
228     match config.compile(engine, bytes) {
229         Ok(module) => Some(module),
230         Err(_) if !known_valid => None,
231         Err(e) => {
232             if let generators::InstanceAllocationStrategy::Pooling { .. } =
233                 &config.wasmtime.strategy
234             {
235                 // When using the pooling allocator, accept failures to compile when arbitrary
236                 // table element limits have been exceeded as there is currently no way
237                 // to constrain the generated module table types.
238                 let string = e.to_string();
239                 if string.contains("minimum element size") {
240                     return None;
241                 }
242             }
243 
244             panic!("failed to compile module: {:?}", e);
245         }
246     }
247 }
248 
249 fn instantiate_with_dummy(store: &mut Store<StoreLimits>, module: &Module) -> Option<Instance> {
250     // Creation of imports can fail due to resource limit constraints, and then
251     // instantiation can naturally fail for a number of reasons as well. Bundle
252     // the two steps together to match on the error below.
253     let instance =
254         dummy::dummy_linker(store, module).and_then(|l| l.instantiate(&mut *store, module));
255 
256     let e = match instance {
257         Ok(i) => return Some(i),
258         Err(e) => e,
259     };
260 
261     // If the instantiation hit OOM for some reason then that's ok, it's
262     // expected that fuzz-generated programs try to allocate lots of
263     // stuff.
264     if store.data().oom {
265         return None;
266     }
267 
268     // Allow traps which can happen normally with `unreachable` or a
269     // timeout or such
270     if e.downcast_ref::<Trap>().is_some() {
271         return None;
272     }
273 
274     let string = e.to_string();
275     // Also allow errors related to fuel consumption
276     if string.contains("all fuel consumed")
277         // Currently we instantiate with a `Linker` which can't instantiate
278         // every single module under the sun due to using name-based resolution
279         // rather than positional-based resolution
280         || string.contains("incompatible import type")
281     {
282         return None;
283     }
284 
285     // Also allow failures to instantiate as a result of hitting instance limits
286     if string.contains("concurrent instances has been reached") {
287         return None;
288     }
289 
290     // Everything else should be a bug in the fuzzer or a bug in wasmtime
291     panic!("failed to instantiate: {:?}", e);
292 }
293 
294 /// Instantiate the given Wasm module with each `Config` and call all of its
295 /// exports. Modulo OOM, non-canonical NaNs, and usage of Wasm features that are
296 /// or aren't enabled for different configs, we should get the same results when
297 /// we call the exported functions for all of our different configs.
298 ///
299 /// Returns `None` if a fuzz configuration was rejected (should happen rarely).
300 pub fn differential_execution(
301     wasm: &[u8],
302     module_config: &generators::ModuleConfig,
303     configs: &[generators::WasmtimeConfig],
304 ) -> Option<()> {
305     use std::collections::{HashMap, HashSet};
306 
307     // We need at least two configs.
308     if configs.len() < 2
309         // And all the configs should be unique.
310         || configs.iter().collect::<HashSet<_>>().len() != configs.len()
311     {
312         return None;
313     }
314 
315     let mut export_func_results: HashMap<String, Result<Box<[Val]>, Trap>> = Default::default();
316     log_wasm(&wasm);
317 
318     for fuzz_config in configs {
319         let fuzz_config = generators::Config {
320             module_config: module_config.clone(),
321             wasmtime: fuzz_config.clone(),
322         };
323         log::debug!("fuzz config: {:?}", fuzz_config);
324 
325         let mut store = fuzz_config.to_store();
326         let module = compile_module(store.engine(), &wasm, true, &fuzz_config)?;
327 
328         // TODO: we should implement tracing versions of these dummy imports
329         // that record a trace of the order that imported functions were called
330         // in and with what values. Like the results of exported functions,
331         // calls to imports should also yield the same values for each
332         // configuration, and we should assert that.
333         let instance = match instantiate_with_dummy(&mut store, &module) {
334             Some(instance) => instance,
335             None => continue,
336         };
337 
338         let exports = instance
339             .exports(&mut store)
340             .filter_map(|e| {
341                 let name = e.name().to_string();
342                 e.into_func().map(|f| (name, f))
343             })
344             .collect::<Vec<_>>();
345         for (name, f) in exports {
346             log::debug!("invoke export {:?}", name);
347             let ty = f.ty(&store);
348             let params = dummy::dummy_values(ty.params());
349             let mut results = vec![Val::I32(0); ty.results().len()];
350             let this_result = f
351                 .call(&mut store, &params, &mut results)
352                 .map(|()| results.into())
353                 .map_err(|e| e.downcast::<Trap>().unwrap());
354 
355             let existing_result = export_func_results
356                 .entry(name.to_string())
357                 .or_insert_with(|| this_result.clone());
358             assert_same_export_func_result(&existing_result, &this_result, &name);
359         }
360     }
361 
362     return Some(());
363 
364     fn assert_same_export_func_result(
365         lhs: &Result<Box<[Val]>, Trap>,
366         rhs: &Result<Box<[Val]>, Trap>,
367         func_name: &str,
368     ) {
369         let fail = || {
370             panic!(
371                 "differential fuzzing failed: exported func {} returned two \
372                  different results: {:?} != {:?}",
373                 func_name, lhs, rhs
374             )
375         };
376 
377         match (lhs, rhs) {
378             (Err(a), Err(b)) => {
379                 if a.trap_code() != b.trap_code() {
380                     fail();
381                 }
382             }
383             (Ok(lhs), Ok(rhs)) => {
384                 if lhs.len() != rhs.len() {
385                     fail();
386                 }
387                 for (lhs, rhs) in lhs.iter().zip(rhs.iter()) {
388                     match (lhs, rhs) {
389                         (Val::I32(lhs), Val::I32(rhs)) if lhs == rhs => continue,
390                         (Val::I64(lhs), Val::I64(rhs)) if lhs == rhs => continue,
391                         (Val::V128(lhs), Val::V128(rhs)) if lhs == rhs => continue,
392                         (Val::F32(lhs), Val::F32(rhs)) if f32_equal(*lhs, *rhs) => continue,
393                         (Val::F64(lhs), Val::F64(rhs)) if f64_equal(*lhs, *rhs) => continue,
394                         (Val::ExternRef(_), Val::ExternRef(_))
395                         | (Val::FuncRef(_), Val::FuncRef(_)) => continue,
396                         _ => fail(),
397                     }
398                 }
399             }
400             _ => fail(),
401         }
402     }
403 }
404 
405 fn f32_equal(a: u32, b: u32) -> bool {
406     let a = f32::from_bits(a);
407     let b = f32::from_bits(b);
408     a == b || (a.is_nan() && b.is_nan())
409 }
410 
411 fn f64_equal(a: u64, b: u64) -> bool {
412     let a = f64::from_bits(a);
413     let b = f64::from_bits(b);
414     a == b || (a.is_nan() && b.is_nan())
415 }
416 
417 /// Invoke the given API calls.
418 pub fn make_api_calls(api: generators::api::ApiCalls) {
419     use crate::generators::api::ApiCall;
420     use std::collections::HashMap;
421 
422     let mut store: Option<Store<StoreLimits>> = None;
423     let mut modules: HashMap<usize, Module> = Default::default();
424     let mut instances: HashMap<usize, Instance> = Default::default();
425 
426     for call in api.calls {
427         match call {
428             ApiCall::StoreNew(config) => {
429                 log::trace!("creating store");
430                 assert!(store.is_none());
431                 store = Some(config.to_store());
432             }
433 
434             ApiCall::ModuleNew { id, wasm } => {
435                 log::debug!("creating module: {}", id);
436                 log_wasm(&wasm);
437                 let module = match Module::new(store.as_ref().unwrap().engine(), &wasm) {
438                     Ok(m) => m,
439                     Err(_) => continue,
440                 };
441                 let old = modules.insert(id, module);
442                 assert!(old.is_none());
443             }
444 
445             ApiCall::ModuleDrop { id } => {
446                 log::trace!("dropping module: {}", id);
447                 drop(modules.remove(&id));
448             }
449 
450             ApiCall::InstanceNew { id, module } => {
451                 log::trace!("instantiating module {} as {}", module, id);
452                 let module = match modules.get(&module) {
453                     Some(m) => m,
454                     None => continue,
455                 };
456 
457                 let store = store.as_mut().unwrap();
458                 if let Some(instance) = instantiate_with_dummy(store, module) {
459                     instances.insert(id, instance);
460                 }
461             }
462 
463             ApiCall::InstanceDrop { id } => {
464                 log::trace!("dropping instance {}", id);
465                 drop(instances.remove(&id));
466             }
467 
468             ApiCall::CallExportedFunc { instance, nth } => {
469                 log::trace!("calling instance export {} / {}", instance, nth);
470                 let instance = match instances.get(&instance) {
471                     Some(i) => i,
472                     None => {
473                         // Note that we aren't guaranteed to instantiate valid
474                         // modules, see comments in `InstanceNew` for details on
475                         // that. But the API call generator can't know if
476                         // instantiation failed, so we might not actually have
477                         // this instance. When that's the case, just skip the
478                         // API call and keep going.
479                         continue;
480                     }
481                 };
482                 let store = store.as_mut().unwrap();
483 
484                 let funcs = instance
485                     .exports(&mut *store)
486                     .filter_map(|e| match e.into_extern() {
487                         Extern::Func(f) => Some(f.clone()),
488                         _ => None,
489                     })
490                     .collect::<Vec<_>>();
491 
492                 if funcs.is_empty() {
493                     continue;
494                 }
495 
496                 let nth = nth % funcs.len();
497                 let f = &funcs[nth];
498                 let ty = f.ty(&store);
499                 let params = dummy::dummy_values(ty.params());
500                 let mut results = vec![Val::I32(0); ty.results().len()];
501                 let _ = f.call(store, &params, &mut results);
502             }
503         }
504     }
505 }
506 
507 /// Executes the wast `test` spectest with the `config` specified.
508 ///
509 /// Ensures that spec tests pass regardless of the `Config`.
510 pub fn spectest(mut fuzz_config: generators::Config, test: generators::SpecTest) {
511     crate::init_fuzzing();
512     fuzz_config.set_spectest_compliant();
513     log::debug!("running {:?} with {:?}", test.file, fuzz_config);
514     let mut wast_context = WastContext::new(fuzz_config.to_store());
515     wast_context.register_spectest().unwrap();
516     wast_context
517         .run_buffer(test.file, test.contents.as_bytes())
518         .unwrap();
519 }
520 
521 /// Execute a series of `table.get` and `table.set` operations.
522 pub fn table_ops(mut fuzz_config: generators::Config, ops: generators::table_ops::TableOps) {
523     let expected_drops = Arc::new(AtomicUsize::new(ops.num_params() as usize));
524     let num_dropped = Arc::new(AtomicUsize::new(0));
525 
526     {
527         fuzz_config.wasmtime.consume_fuel = true;
528         let mut store = fuzz_config.to_store();
529 
530         // consume the default fuel in the store ...
531         let remaining = store.consume_fuel(0).unwrap();
532         store.consume_fuel(remaining - 1).unwrap();
533         // ... then add back in how much fuel we're allowing here
534         store.add_fuel(1_000).unwrap();
535 
536         let wasm = ops.to_wasm_binary();
537         log_wasm(&wasm);
538         let module = match compile_module(store.engine(), &wasm, false, &fuzz_config) {
539             Some(m) => m,
540             None => return,
541         };
542 
543         let mut linker = Linker::new(store.engine());
544 
545         // To avoid timeouts, limit the number of explicit GCs we perform per
546         // test case.
547         const MAX_GCS: usize = 5;
548 
549         let num_gcs = AtomicUsize::new(0);
550         linker
551             .define(
552                 "",
553                 "gc",
554                 // NB: use `Func::new` so that this can still compile on the old x86
555                 // backend, where `IntoFunc` isn't implemented for multi-value
556                 // returns.
557                 Func::new(
558                     &mut store,
559                     FuncType::new(
560                         vec![],
561                         vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
562                     ),
563                     {
564                         let num_dropped = num_dropped.clone();
565                         let expected_drops = expected_drops.clone();
566                         move |mut caller: Caller<'_, StoreLimits>, _params, results| {
567                             if num_gcs.fetch_add(1, SeqCst) < MAX_GCS {
568                                 caller.gc();
569                             }
570 
571                             expected_drops.fetch_add(3, SeqCst);
572                             results[0] =
573                                 Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
574                             results[1] =
575                                 Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
576                             results[2] =
577                                 Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
578                             Ok(())
579                         }
580                     },
581                 ),
582             )
583             .unwrap();
584 
585         linker
586             .func_wrap("", "take_refs", {
587                 let expected_drops = expected_drops.clone();
588                 move |a: Option<ExternRef>, b: Option<ExternRef>, c: Option<ExternRef>| {
589                     // Do the assertion on each ref's inner data, even though it
590                     // all points to the same atomic, so that if we happen to
591                     // run into a use-after-free bug with one of these refs we
592                     // are more likely to trigger a segfault.
593                     if let Some(a) = a {
594                         let a = a.data().downcast_ref::<CountDrops>().unwrap();
595                         assert!(a.0.load(SeqCst) <= expected_drops.load(SeqCst));
596                     }
597                     if let Some(b) = b {
598                         let b = b.data().downcast_ref::<CountDrops>().unwrap();
599                         assert!(b.0.load(SeqCst) <= expected_drops.load(SeqCst));
600                     }
601                     if let Some(c) = c {
602                         let c = c.data().downcast_ref::<CountDrops>().unwrap();
603                         assert!(c.0.load(SeqCst) <= expected_drops.load(SeqCst));
604                     }
605                 }
606             })
607             .unwrap();
608 
609         linker
610             .define(
611                 "",
612                 "make_refs",
613                 // NB: use `Func::new` so that this can still compile on the old
614                 // x86 backend, where `IntoFunc` isn't implemented for
615                 // multi-value returns.
616                 Func::new(
617                     &mut store,
618                     FuncType::new(
619                         vec![],
620                         vec![ValType::ExternRef, ValType::ExternRef, ValType::ExternRef],
621                     ),
622                     {
623                         let num_dropped = num_dropped.clone();
624                         let expected_drops = expected_drops.clone();
625                         move |_caller, _params, results| {
626                             expected_drops.fetch_add(3, SeqCst);
627                             results[0] =
628                                 Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
629                             results[1] =
630                                 Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
631                             results[2] =
632                                 Some(ExternRef::new(CountDrops(num_dropped.clone()))).into();
633                             Ok(())
634                         }
635                     },
636                 ),
637             )
638             .unwrap();
639 
640         let instance = linker.instantiate(&mut store, &module).unwrap();
641         let run = instance.get_func(&mut store, "run").unwrap();
642 
643         let args: Vec<_> = (0..ops.num_params())
644             .map(|_| Val::ExternRef(Some(ExternRef::new(CountDrops(num_dropped.clone())))))
645             .collect();
646         let _ = run.call(&mut store, &args, &mut []);
647 
648         // Do a final GC after running the Wasm.
649         store.gc();
650     }
651 
652     assert_eq!(num_dropped.load(SeqCst), expected_drops.load(SeqCst));
653     return;
654 
655     struct CountDrops(Arc<AtomicUsize>);
656 
657     impl Drop for CountDrops {
658         fn drop(&mut self) {
659             self.0.fetch_add(1, SeqCst);
660         }
661     }
662 }
663 
664 /// Perform differential execution between Cranelift and wasmi, diffing the
665 /// resulting memory image when execution terminates. This relies on the
666 /// module-under-test to be instrumented to bound the execution time. Invoke
667 /// with a module generated by `wasm-smith` using the
668 /// `SingleFunctionModuleConfig` configuration type for best results.
669 ///
670 /// May return `None` if we early-out due to a rejected fuzz config; these
671 /// should be rare if modules are generated appropriately.
672 pub fn differential_wasmi_execution(wasm: &[u8], config: &generators::Config) -> Option<()> {
673     crate::init_fuzzing();
674     log_wasm(wasm);
675 
676     // Instantiate wasmi module and instance.
677     let wasmi_module = wasmi::Module::from_buffer(&wasm[..]).ok()?;
678     let wasmi_instance =
679         wasmi::ModuleInstance::new(&wasmi_module, &wasmi::ImportsBuilder::default()).ok()?;
680     let wasmi_instance = wasmi_instance.assert_no_start();
681 
682     // If wasmi succeeded then we assert that wasmtime will also succeed.
683     let (wasmtime_module, mut wasmtime_store) = differential_store(wasm, config);
684     let wasmtime_module = wasmtime_module?;
685     let wasmtime_instance = Instance::new(&mut wasmtime_store, &wasmtime_module, &[])
686         .expect("Wasmtime can instantiate module");
687 
688     // Introspect wasmtime module to find name of an exported function and of an
689     // exported memory.
690     let (func_name, ty) = first_exported_function(&wasmtime_module)?;
691     let memory_name = first_exported_memory(&wasmtime_module)?;
692 
693     let wasmi_mem_export = wasmi_instance.export_by_name(memory_name).unwrap();
694     let wasmi_mem = wasmi_mem_export.as_memory().unwrap();
695     let wasmi_main_export = wasmi_instance.export_by_name(func_name).unwrap();
696     let wasmi_main = wasmi_main_export.as_func().unwrap();
697     let wasmi_val = wasmi::FuncInstance::invoke(&wasmi_main, &[], &mut wasmi::NopExternals);
698 
699     let wasmtime_mem = wasmtime_instance
700         .get_memory(&mut wasmtime_store, memory_name)
701         .expect("memory export is present");
702     let wasmtime_main = wasmtime_instance
703         .get_func(&mut wasmtime_store, func_name)
704         .expect("function export is present");
705     let mut wasmtime_results = vec![Val::I32(0); ty.results().len()];
706     let wasmtime_val = wasmtime_main
707         .call(&mut wasmtime_store, &[], &mut wasmtime_results)
708         .map(|()| wasmtime_results.get(0).cloned());
709 
710     debug!(
711         "Successful execution: wasmi returned {:?}, wasmtime returned {:?}",
712         wasmi_val, wasmtime_val
713     );
714 
715     match (&wasmi_val, &wasmtime_val) {
716         (&Ok(Some(wasmi::RuntimeValue::I32(a))), &Ok(Some(Val::I32(b)))) if a == b => {}
717         (&Ok(Some(wasmi::RuntimeValue::F32(a))), &Ok(Some(Val::F32(b))))
718             if f32_equal(a.to_bits(), b) => {}
719         (&Ok(Some(wasmi::RuntimeValue::I64(a))), &Ok(Some(Val::I64(b)))) if a == b => {}
720         (&Ok(Some(wasmi::RuntimeValue::F64(a))), &Ok(Some(Val::F64(b))))
721             if f64_equal(a.to_bits(), b) => {}
722         (&Ok(None), &Ok(None)) => {}
723         (&Err(_), &Err(_)) => {}
724         _ => {
725             panic!(
726                 "Values do not match: wasmi returned {:?}; wasmtime returned {:?}",
727                 wasmi_val, wasmtime_val
728             );
729         }
730     }
731 
732     if wasmi_mem.current_size().0 != wasmtime_mem.size(&wasmtime_store) as usize {
733         panic!("resulting memories are not the same size");
734     }
735 
736     // Wasmi memory may be stored non-contiguously; copy it out to a contiguous chunk.
737     let mut wasmi_buf: Vec<u8> = vec![0; wasmtime_mem.data_size(&wasmtime_store)];
738     wasmi_mem
739         .get_into(0, &mut wasmi_buf[..])
740         .expect("can access wasmi memory");
741 
742     let wasmtime_slice = wasmtime_mem.data(&wasmtime_store);
743 
744     if wasmi_buf.len() >= 64 {
745         debug!("-> First 64 bytes of wasmi heap: {:?}", &wasmi_buf[0..64]);
746         debug!(
747             "-> First 64 bytes of Wasmtime heap: {:?}",
748             &wasmtime_slice[0..64]
749         );
750     }
751 
752     if &wasmi_buf[..] != &wasmtime_slice[..] {
753         panic!("memory contents are not equal");
754     }
755 
756     Some(())
757 }
758 
759 /// Perform differential execution between Wasmtime and the official WebAssembly
760 /// specification interpreter.
761 ///
762 /// May return `None` if we early-out due to a rejected fuzz config.
763 pub fn differential_spec_execution(wasm: &[u8], config: &generators::Config) -> Option<()> {
764     crate::init_fuzzing();
765     debug!("config: {:#?}", config);
766     log_wasm(wasm);
767 
768     // Run the spec interpreter first, then Wasmtime. The order is important
769     // because both sides (OCaml runtime and Wasmtime) register signal handlers;
770     // Wasmtime uses these signal handlers for catching various WebAssembly
771     // failures. On certain OSes (e.g. Linux x86_64), the signal handlers
772     // interfere, observable as an uncaught `SIGSEGV`--not even caught by
773     // libFuzzer. By running Wasmtime second, its signal handlers are registered
774     // most recently and they catch failures appropriately.
775     let spec_vals = wasm_spec_interpreter::interpret(wasm, vec![]);
776     debug!("spec interpreter returned: {:?}", &spec_vals);
777     let wasmtime_vals = run_in_wasmtime(wasm, config, &[]);
778     debug!("Wasmtime returned: {:?}", wasmtime_vals);
779 
780     // Match a spec interpreter value against a Wasmtime value. Eventually this
781     // should support references and `v128` (TODO).
782     fn matches(spec_val: &wasm_spec_interpreter::Value, wasmtime_val: &wasmtime::Val) -> bool {
783         match (spec_val, wasmtime_val) {
784             (wasm_spec_interpreter::Value::I32(a), wasmtime::Val::I32(b)) => a == b,
785             (wasm_spec_interpreter::Value::I64(a), wasmtime::Val::I64(b)) => a == b,
786             (wasm_spec_interpreter::Value::F32(a), wasmtime::Val::F32(b)) => {
787                 f32_equal(*a as u32, *b)
788             }
789             (wasm_spec_interpreter::Value::F64(a), wasmtime::Val::F64(b)) => {
790                 f64_equal(*a as u64, *b)
791             }
792             (_, _) => unreachable!("fuzzing non-scalar value types is still TODO"),
793         }
794     }
795 
796     match (&spec_vals, &wasmtime_vals) {
797         // Compare the returned values, failing if they do not match.
798         (Ok(spec_vals), Ok(Some(wasmtime_vals))) => {
799             let all_match = spec_vals
800                 .iter()
801                 .zip(wasmtime_vals)
802                 .all(|(s, w)| matches(s, w));
803             if !all_match {
804                 panic!(
805                     "Values do not match: spec returned {:?}; wasmtime returned {:?}",
806                     spec_vals, wasmtime_vals
807                 );
808             }
809         }
810         (_, Ok(None)) => {
811             // `run_in_wasmtime` rejected the config
812             return None;
813         }
814         // If both sides fail, skip this fuzz execution.
815         (Err(spec_error), Err(wasmtime_error)) => {
816             // The `None` value returned here indicates that both sides
817             // failed--if we see too many of these we might be failing too often
818             // to check instruction semantics. At some point it would be
819             // beneficial to compare the error messages from both sides (TODO).
820             // It would also be good to keep track of statistics about the
821             // ratios of the kinds of errors the fuzzer sees (TODO).
822             warn!(
823                 "Both sides failed: spec returned '{}'; wasmtime returned {:?}",
824                 spec_error, wasmtime_error
825             );
826             return None;
827         }
828         // If only one side fails, fail the fuzz the test.
829         _ => {
830             panic!(
831                 "Only one side failed: spec returned {:?}; wasmtime returned {:?}",
832                 &spec_vals, &wasmtime_vals
833             );
834         }
835     }
836 
837     // TODO Compare memory contents.
838 
839     Some(())
840 }
841 
842 fn differential_store(
843     wasm: &[u8],
844     fuzz_config: &generators::Config,
845 ) -> (Option<Module>, Store<StoreLimits>) {
846     let store = fuzz_config.to_store();
847     let module = compile_module(store.engine(), wasm, true, fuzz_config);
848     (module, store)
849 }
850 
851 /// Helper for instantiating and running a Wasm module in Wasmtime and returning
852 /// its `Val` results.
853 fn run_in_wasmtime(
854     wasm: &[u8],
855     config: &generators::Config,
856     params: &[Val],
857 ) -> anyhow::Result<Option<Vec<Val>>> {
858     // Instantiate wasmtime module and instance.
859     let (wasmtime_module, mut wasmtime_store) = differential_store(wasm, config);
860     let wasmtime_module = match wasmtime_module {
861         Some(m) => m,
862         None => return Ok(None),
863     };
864 
865     let wasmtime_instance = Instance::new(&mut wasmtime_store, &wasmtime_module, &[])
866         .context("Wasmtime cannot instantiate module")?;
867 
868     // Find the first exported function.
869     let (func_name, ty) =
870         first_exported_function(&wasmtime_module).context("Cannot find exported function")?;
871     let wasmtime_main = wasmtime_instance
872         .get_func(&mut wasmtime_store, &func_name[..])
873         .expect("function export is present");
874 
875     // Execute the function and return the values.
876     let mut results = vec![Val::I32(0); ty.results().len()];
877     wasmtime_main
878         .call(&mut wasmtime_store, params, &mut results)
879         .map(|()| Some(results))
880 }
881 
882 // Introspect wasmtime module to find the name of the first exported function.
883 fn first_exported_function(module: &wasmtime::Module) -> Option<(&str, FuncType)> {
884     for e in module.exports() {
885         match e.ty() {
886             wasmtime::ExternType::Func(ty) => return Some((e.name(), ty)),
887             _ => {}
888         }
889     }
890     None
891 }
892 
893 fn first_exported_memory(module: &Module) -> Option<&str> {
894     for e in module.exports() {
895         match e.ty() {
896             wasmtime::ExternType::Memory(..) => return Some(e.name()),
897             _ => {}
898         }
899     }
900     None
901 }
902 
903 #[derive(Default)]
904 struct SignalOnDrop {
905     state: Arc<(Mutex<bool>, Condvar)>,
906     thread: Option<std::thread::JoinHandle<()>>,
907 }
908 
909 impl SignalOnDrop {
910     fn spawn_timeout(&mut self, dur: Duration, closure: impl FnOnce() + Send + 'static) {
911         let state = self.state.clone();
912         let start = Instant::now();
913         self.thread = Some(std::thread::spawn(move || {
914             // Using our mutex/condvar we wait here for the first of `dur` to
915             // pass or the `SignalOnDrop` instance to get dropped.
916             let (lock, cvar) = &*state;
917             let mut signaled = lock.lock().unwrap();
918             while !*signaled {
919                 // Adjust our requested `dur` based on how much time has passed.
920                 let dur = match dur.checked_sub(start.elapsed()) {
921                     Some(dur) => dur,
922                     None => break,
923                 };
924                 let (lock, result) = cvar.wait_timeout(signaled, dur).unwrap();
925                 signaled = lock;
926                 // If we timed out for sure then there's no need to continue
927                 // since we'll just abort on the next `checked_sub` anyway.
928                 if result.timed_out() {
929                     break;
930                 }
931             }
932             drop(signaled);
933 
934             closure();
935         }));
936     }
937 }
938 
939 impl Drop for SignalOnDrop {
940     fn drop(&mut self) {
941         if let Some(thread) = self.thread.take() {
942             let (lock, cvar) = &*self.state;
943             // Signal our thread that we've been dropped and wake it up if it's
944             // blocked.
945             let mut g = lock.lock().unwrap();
946             *g = true;
947             cvar.notify_one();
948             drop(g);
949 
950             // ... and then wait for the thread to exit to ensure we clean up
951             // after ourselves.
952             thread.join().unwrap();
953         }
954     }
955 }
956