wasmtime_fuzzing/generators/
api.rs1use crate::generators::Config;
18use arbitrary::{Arbitrary, Unstructured};
19use std::collections::BTreeSet;
20
21#[derive(Arbitrary, Debug)]
22struct 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#[derive(Arbitrary, Debug)]
32#[expect(missing_docs, reason = "self-describing fields")]
33pub 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}
41use ApiCall::*;
42
43struct Scope {
44 id_counter: usize,
45 modules: BTreeSet<usize>,
46 instances: BTreeSet<usize>,
47 config: Config,
48}
49
50impl 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#[derive(Debug)]
60pub struct ApiCalls {
61 pub calls: Vec<ApiCall>,
63}
64
65impl<'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 calls.push(StoreNew(config.clone()));
74
75 let mut scope = Scope {
76 id_counter: 0,
77 modules: BTreeSet::default(),
78 instances: BTreeSet::default(),
79 config,
80 };
81
82 let max_calls = 100;
85
86 for _ in 0..input.arbitrary_len::<ApiCall>()? {
87 if calls.len() > max_calls {
88 break;
89 }
90
91 let mut choices: Vec<fn(_, &mut Scope) -> arbitrary::Result<ApiCall>> = vec![];
92
93 if swarm.module_new {
94 choices.push(|input, scope| {
95 let id = scope.next_id();
96 let wasm = scope.config.generate(input, Some(1000))?;
97 scope.modules.insert(id);
98 Ok(ModuleNew {
99 id,
100 wasm: wasm.to_bytes(),
101 })
102 });
103 }
104 if swarm.module_drop && !scope.modules.is_empty() {
105 choices.push(|input, scope| {
106 let modules: Vec<_> = scope.modules.iter().collect();
107 let id = **input.choose(&modules)?;
108 scope.modules.remove(&id);
109 Ok(ModuleDrop { id })
110 });
111 }
112 if swarm.instance_new && !scope.modules.is_empty() {
113 choices.push(|input, scope| {
114 let modules: Vec<_> = scope.modules.iter().collect();
115 let module = **input.choose(&modules)?;
116 let id = scope.next_id();
117 scope.instances.insert(id);
118 Ok(InstanceNew { id, module })
119 });
120 }
121 if swarm.instance_drop && !scope.instances.is_empty() {
122 choices.push(|input, scope| {
123 let instances: Vec<_> = scope.instances.iter().collect();
124 let id = **input.choose(&instances)?;
125 scope.instances.remove(&id);
126 Ok(InstanceDrop { id })
127 });
128 }
129 if swarm.call_exported_func && !scope.instances.is_empty() {
130 choices.push(|input, scope| {
131 let instances: Vec<_> = scope.instances.iter().collect();
132 let instance = **input.choose(&instances)?;
133 let nth = usize::arbitrary(input)?;
134 Ok(CallExportedFunc { instance, nth })
135 });
136 }
137
138 if choices.is_empty() {
139 break;
140 }
141 let c = input.choose(&choices)?;
142 calls.push(c(input, &mut scope)?);
143 }
144
145 Ok(ApiCalls { calls })
146 }
147}