/src/wasm-tools/crates/wasm-mutate/src/mutators/custom.rs
Line | Count | Source |
1 | | //! Mutate custom sections. |
2 | | |
3 | | use super::Mutator; |
4 | | use rand::prelude::*; |
5 | | use std::borrow::Cow; |
6 | | |
7 | | #[derive(Clone, Copy)] |
8 | | pub struct CustomSectionMutator; |
9 | | |
10 | | impl Mutator for CustomSectionMutator { |
11 | 280 | fn can_mutate(&self, config: &crate::WasmMutate) -> bool { |
12 | 280 | config.info().has_custom_section() |
13 | 280 | } |
14 | | |
15 | 0 | fn mutate<'a>( |
16 | 0 | &self, |
17 | 0 | config: &'a mut crate::WasmMutate, |
18 | 0 | ) -> crate::Result<Box<dyn Iterator<Item = crate::Result<wasm_encoder::Module>> + 'a>> { |
19 | 0 | let custom_section_indices: Vec<_> = config |
20 | 0 | .info() |
21 | 0 | .raw_sections |
22 | 0 | .iter() |
23 | 0 | .enumerate() |
24 | 0 | .filter(|(_i, s)| s.id == wasm_encoder::SectionId::Custom as u8) |
25 | 0 | .map(|(i, _s)| i) |
26 | 0 | .collect(); |
27 | 0 | assert!(!custom_section_indices.is_empty()); |
28 | | |
29 | 0 | let custom_section_index = *custom_section_indices.choose(config.rng()).unwrap(); |
30 | 0 | let reader = config.info().get_binary_reader(custom_section_index); |
31 | 0 | let old_custom_section = wasmparser::CustomSectionReader::new(reader).unwrap(); |
32 | | |
33 | | let name_string; |
34 | | let data_vec; |
35 | 0 | let mut name = old_custom_section.name(); |
36 | 0 | let mut data = old_custom_section.data(); |
37 | | |
38 | 0 | if config.rng().random_ratio(1, 20) { |
39 | | // Mutate the custom section's name. |
40 | 0 | let mut new_name = name.to_string().into_bytes(); |
41 | 0 | config.raw_mutate( |
42 | 0 | &mut new_name, |
43 | 0 | if config.reduce { |
44 | 0 | name.len().saturating_sub(1) |
45 | | } else { |
46 | 0 | std::cmp::max(name.len() * 2, 32) |
47 | | }, |
48 | 0 | )?; |
49 | 0 | name_string = String::from_utf8_lossy(&new_name).to_string(); |
50 | 0 | name = &name_string; |
51 | | } else { |
52 | | // Mutate the custom section's data. |
53 | 0 | let mut new_data = data.to_vec(); |
54 | 0 | config.raw_mutate( |
55 | 0 | &mut new_data, |
56 | 0 | if config.reduce { |
57 | 0 | data.len().saturating_sub(1) |
58 | | } else { |
59 | 0 | std::cmp::max(data.len() * 2, 32) |
60 | | }, |
61 | 0 | )?; |
62 | 0 | data_vec = new_data; |
63 | 0 | data = &data_vec; |
64 | | }; |
65 | | |
66 | 0 | Ok(Box::new(std::iter::once(Ok(config |
67 | 0 | .info() |
68 | 0 | .replace_section( |
69 | 0 | custom_section_index, |
70 | 0 | &wasm_encoder::CustomSection { |
71 | 0 | name: name.into(), |
72 | 0 | data: Cow::Borrowed(data), |
73 | 0 | }, |
74 | 0 | ))))) |
75 | 0 | } |
76 | | } |
77 | | |
78 | | #[derive(Clone, Copy)] |
79 | | pub struct AddCustomSectionMutator; |
80 | | |
81 | | const MAX_NEW_DATA_LEN: usize = 100; |
82 | | const MAX_NEW_NAME_LEN: usize = 20; |
83 | | |
84 | | impl Mutator for AddCustomSectionMutator { |
85 | 275 | fn can_mutate(&self, config: &crate::WasmMutate) -> bool { |
86 | 275 | !config.reduce |
87 | 275 | } |
88 | | |
89 | 275 | fn mutate<'a>( |
90 | 275 | &self, |
91 | 275 | config: &'a mut crate::WasmMutate, |
92 | 275 | ) -> crate::Result<Box<dyn Iterator<Item = crate::Result<wasm_encoder::Module>> + 'a>> { |
93 | 275 | let num_sections = config.info().raw_sections.len(); |
94 | 275 | let new_custom_section_idx = config.rng().random_range(0..=num_sections); |
95 | 275 | let mut name = vec![]; |
96 | 275 | config.raw_mutate(&mut name, MAX_NEW_NAME_LEN)?; |
97 | 275 | let name = String::from_utf8_lossy(&name); |
98 | 275 | let mut data = vec![]; |
99 | 275 | config.raw_mutate(&mut data, MAX_NEW_DATA_LEN)?; |
100 | | |
101 | 275 | Ok(Box::new(std::iter::once(Ok(config.info().insert_section( |
102 | 275 | new_custom_section_idx, |
103 | 275 | &wasm_encoder::CustomSection { |
104 | 275 | name: name.into(), |
105 | 275 | data: Cow::Borrowed(&data), |
106 | 275 | }, |
107 | 275 | ))))) |
108 | 275 | } |
109 | | } |
110 | | |
111 | | #[derive(Copy, Clone)] |
112 | | pub struct ReorderCustomSectionMutator; |
113 | | |
114 | | impl Mutator for ReorderCustomSectionMutator { |
115 | 62 | fn can_mutate(&self, config: &crate::WasmMutate) -> bool { |
116 | 62 | config.info().has_custom_section() && config.info().raw_sections.len() > 1 |
117 | 62 | } |
118 | | |
119 | 0 | fn mutate<'a>( |
120 | 0 | &self, |
121 | 0 | config: &'a mut crate::WasmMutate, |
122 | 0 | ) -> crate::Result<Box<dyn Iterator<Item = crate::Result<wasm_encoder::Module>> + 'a>> { |
123 | 0 | let custom_section_indices: Vec<_> = config |
124 | 0 | .info() |
125 | 0 | .raw_sections |
126 | 0 | .iter() |
127 | 0 | .enumerate() |
128 | 0 | .filter(|(_i, s)| s.id == wasm_encoder::SectionId::Custom as u8) |
129 | 0 | .map(|(i, _s)| i) |
130 | 0 | .collect(); |
131 | 0 | assert!(!custom_section_indices.is_empty()); |
132 | | |
133 | 0 | let src_idx = *custom_section_indices.choose(config.rng()).unwrap(); |
134 | 0 | let num_sections = config.info().raw_sections.len(); |
135 | | let mut dest_idx; |
136 | | loop { |
137 | 0 | dest_idx = config.rng().random_range(0..num_sections); |
138 | 0 | if dest_idx != src_idx { |
139 | 0 | break; |
140 | 0 | } |
141 | 0 | config.consume_fuel(1)?; |
142 | | } |
143 | | |
144 | 0 | Ok(Box::new(std::iter::once(Ok(config |
145 | 0 | .info() |
146 | 0 | .move_section(src_idx, dest_idx))))) |
147 | 0 | } |
148 | | } |
149 | | |
150 | | #[cfg(test)] |
151 | | mod tests { |
152 | | use super::*; |
153 | | |
154 | | #[test] |
155 | | fn test_add_custom_section() { |
156 | | crate::mutators::match_mutation( |
157 | | r#" |
158 | | (module) |
159 | | "#, |
160 | | AddCustomSectionMutator, |
161 | | r#" |
162 | | (module |
163 | | (@custom "" "") |
164 | | ) |
165 | | "#, |
166 | | ); |
167 | | } |
168 | | |
169 | | #[test] |
170 | | fn test_grow_custom_section() { |
171 | | crate::mutators::match_mutation( |
172 | | r#" |
173 | | (module |
174 | | (@custom "name" "data") |
175 | | ) |
176 | | "#, |
177 | | CustomSectionMutator, |
178 | | r#" |
179 | | (module |
180 | | (@custom "name" "datadata") |
181 | | ) |
182 | | "#, |
183 | | ); |
184 | | } |
185 | | |
186 | | #[test] |
187 | | fn test_shrink_custom_section() { |
188 | | crate::mutators::match_mutation( |
189 | | r#" |
190 | | (module |
191 | | (@custom "name" "data") |
192 | | ) |
193 | | "#, |
194 | | CustomSectionMutator, |
195 | | r#" |
196 | | (module |
197 | | (@custom "name" "d") |
198 | | ) |
199 | | "#, |
200 | | ); |
201 | | } |
202 | | |
203 | | #[test] |
204 | | fn test_mutate_custom_section() { |
205 | | crate::mutators::match_mutation( |
206 | | r#" |
207 | | (module |
208 | | (@custom "name" "data") |
209 | | ) |
210 | | "#, |
211 | | CustomSectionMutator, |
212 | | r#" |
213 | | (module |
214 | | (@custom "name" "aaaa") |
215 | | ) |
216 | | "#, |
217 | | ); |
218 | | } |
219 | | |
220 | | #[test] |
221 | | fn test_mutate_custom_section_name() { |
222 | | crate::mutators::match_mutation( |
223 | | r#" |
224 | | (module |
225 | | (@custom "custom-name" "data") |
226 | | ) |
227 | | "#, |
228 | | CustomSectionMutator, |
229 | | r#" |
230 | | (module |
231 | | (@custom "custom" "data") |
232 | | ) |
233 | | "#, |
234 | | ); |
235 | | } |
236 | | |
237 | | #[test] |
238 | | fn test_reorder_custom_section() { |
239 | | crate::mutators::match_mutation( |
240 | | r#" |
241 | | (module |
242 | | (@custom "name2" "data") |
243 | | (@custom "name3" "data") |
244 | | ) |
245 | | "#, |
246 | | ReorderCustomSectionMutator, |
247 | | r#" |
248 | | (module |
249 | | (@custom "name3" "data") |
250 | | (@custom "name2" "data") |
251 | | ) |
252 | | "#, |
253 | | ) |
254 | | } |
255 | | |
256 | | #[test] |
257 | | fn test_reorder_custom_section2() { |
258 | | crate::mutators::match_mutation( |
259 | | r#" |
260 | | (module |
261 | | (func (export "x")) |
262 | | (@custom "name2" "data") |
263 | | (@custom "name3" "data") |
264 | | ) |
265 | | "#, |
266 | | ReorderCustomSectionMutator, |
267 | | r#" |
268 | | (module |
269 | | (@custom "name3" "data") |
270 | | (func (export "x")) |
271 | | (@custom "name2" "data") |
272 | | ) |
273 | | "#, |
274 | | ) |
275 | | } |
276 | | } |