Coverage Report

Created: 2026-09-28 08:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}