1 //! Generating sequences of Wasmtime API calls. 2 //! 3 //! We only generate *valid* sequences of API calls. To do this, we keep track 4 //! of what objects we've already created in earlier API calls via the `Scope` 5 //! struct. 6 //! 7 //! To generate even-more-pathological sequences of API calls, we use [swarm 8 //! testing]: 9 //! 10 //! > In swarm testing, the usual practice of potentially including all features 11 //! > in every test case is abandoned. Rather, a large “swarm” of randomly 12 //! > generated configurations, each of which omits some features, is used, with 13 //! > configurations receiving equal resources. 14 //! 15 //! [swarm testing]: https://www.cs.utah.edu/~regehr/papers/swarm12.pdf 16 17 use crate::generators::{Config, ModuleConfig}; 18 use arbitrary::{Arbitrary, Unstructured}; 19 use std::collections::BTreeSet; 20 21 #[derive(Arbitrary, Debug)] 22 struct Swarm { 23 module_new: bool, 24 module_drop: bool, 25 instance_new: bool, 26 instance_drop: bool, 27 call_exported_func: bool, 28 } 29 30 /// A call to one of Wasmtime's public APIs. 31 #[derive(Arbitrary, Debug)] 32 #[allow(missing_docs)] 33 pub enum ApiCall { 34 StoreNew(Config), 35 ModuleNew { id: usize, wasm: Vec<u8> }, 36 ModuleDrop { id: usize }, 37 InstanceNew { id: usize, module: usize }, 38 InstanceDrop { id: usize }, 39 CallExportedFunc { instance: usize, nth: usize }, 40 } 41 use ApiCall::*; 42 43 struct Scope { 44 id_counter: usize, 45 modules: BTreeSet<usize>, 46 instances: BTreeSet<usize>, 47 module_config: ModuleConfig, 48 } 49 50 impl Scope { 51 fn next_id(&mut self) -> usize { 52 let id = self.id_counter; 53 self.id_counter = id + 1; 54 id 55 } 56 } 57 58 /// A sequence of API calls. 59 #[derive(Debug)] 60 pub struct ApiCalls { 61 /// The API calls. 62 pub calls: Vec<ApiCall>, 63 } 64 65 impl<'a> Arbitrary<'a> for ApiCalls { 66 fn arbitrary(input: &mut Unstructured<'a>) -> arbitrary::Result<Self> { 67 crate::init_fuzzing(); 68 69 let swarm = Swarm::arbitrary(input)?; 70 let mut calls = vec![]; 71 72 let config = Config::arbitrary(input)?; 73 let module_config = config.module_config.clone(); 74 calls.push(StoreNew(config)); 75 76 let mut scope = Scope { 77 id_counter: 0, 78 modules: BTreeSet::default(), 79 instances: BTreeSet::default(), 80 module_config, 81 }; 82 83 // Total limit on number of API calls we'll generate. This exists to 84 // avoid libFuzzer timeouts. 85 let max_calls = 100; 86 87 for _ in 0..input.arbitrary_len::<ApiCall>()? { 88 if calls.len() > max_calls { 89 break; 90 } 91 92 let mut choices: Vec<fn(_, &mut Scope) -> arbitrary::Result<ApiCall>> = vec![]; 93 94 if swarm.module_new { 95 choices.push(|input, scope| { 96 let id = scope.next_id(); 97 let mut wasm = scope.module_config.generate(input)?; 98 wasm.ensure_termination(1000); 99 scope.modules.insert(id); 100 Ok(ModuleNew { 101 id, 102 wasm: wasm.to_bytes(), 103 }) 104 }); 105 } 106 if swarm.module_drop && !scope.modules.is_empty() { 107 choices.push(|input, scope| { 108 let modules: Vec<_> = scope.modules.iter().collect(); 109 let id = **input.choose(&modules)?; 110 scope.modules.remove(&id); 111 Ok(ModuleDrop { id }) 112 }); 113 } 114 if swarm.instance_new && !scope.modules.is_empty() { 115 choices.push(|input, scope| { 116 let modules: Vec<_> = scope.modules.iter().collect(); 117 let module = **input.choose(&modules)?; 118 let id = scope.next_id(); 119 scope.instances.insert(id); 120 Ok(InstanceNew { id, module }) 121 }); 122 } 123 if swarm.instance_drop && !scope.instances.is_empty() { 124 choices.push(|input, scope| { 125 let instances: Vec<_> = scope.instances.iter().collect(); 126 let id = **input.choose(&instances)?; 127 scope.instances.remove(&id); 128 Ok(InstanceDrop { id }) 129 }); 130 } 131 if swarm.call_exported_func && !scope.instances.is_empty() { 132 choices.push(|input, scope| { 133 let instances: Vec<_> = scope.instances.iter().collect(); 134 let instance = **input.choose(&instances)?; 135 let nth = usize::arbitrary(input)?; 136 Ok(CallExportedFunc { instance, nth }) 137 }); 138 } 139 140 if choices.is_empty() { 141 break; 142 } 143 let c = input.choose(&choices)?; 144 calls.push(c(input, &mut scope)?); 145 } 146 147 Ok(ApiCalls { calls }) 148 } 149 } 150