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