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