Coverage Report

Created: 2023-04-25 07:07

/src/wasm-tools/fuzz/fuzz_targets/mutate.rs
Line
Count
Source (jump to first uncovered line)
1
#![no_main]
2
3
use arbitrary::Unstructured;
4
use libfuzzer_sys::fuzz_target;
5
use std::sync::atomic::{AtomicU64, Ordering};
6
use wasmparser::WasmFeatures;
7
8
static NUM_RUNS: AtomicU64 = AtomicU64::new(0);
9
static NUM_SUCCESSFUL_MUTATIONS: AtomicU64 = AtomicU64::new(0);
10
11
fuzz_target!(|bytes: &[u8]| {
12
    let _ = env_logger::try_init();
13
14
    // Generate a random Wasm module with `wasm-smith` as well as a RNG seed for
15
    // use with `wasm-mutate`.
16
17
    let mut seed = 0;
18
    let mut preserve_semantics = false;
19
    let mut u = Unstructured::new(bytes);
20
10.9k
    let (wasm, config) = match wasm_tools_fuzz::generate_valid_module(&mut u, |config, u| {
21
10.9k
        config.exceptions_enabled = false;
22
10.9k
        seed = u.arbitrary()?;
23
10.9k
        preserve_semantics = u.arbitrary()?;
24
10.9k
        Ok(())
25
10.9k
    }) {
26
        Ok(m) => m,
27
        Err(_) => return,
28
    };
29
    log::debug!("seed = {}", seed);
30
31
    // Keep track of how many runs we've done thus far and how many of those
32
    // runs had successful mutations.
33
34
    let old_num_runs = NUM_RUNS.fetch_add(1, Ordering::Relaxed);
35
    if old_num_runs % 4096 == 4095 && log::log_enabled!(log::Level::Info) {
36
        let successful = NUM_SUCCESSFUL_MUTATIONS.load(Ordering::Relaxed);
37
        let percent = successful as f64 / old_num_runs as f64 * 100.0;
38
        log::info!(
39
            "{} / {} ({:.2}%) successful mutations.",
40
            successful,
41
            old_num_runs,
42
            percent
43
        );
44
    }
45
46
    // Mutate the Wasm with `wasm-mutate`. Assert that each mutation is still
47
    // valid Wasm.
48
49
    let mut wasm_mutate = wasm_mutate::WasmMutate::default();
50
    wasm_mutate.seed(seed);
51
    wasm_mutate.fuel(300);
52
    wasm_mutate.preserve_semantics(
53
        // If we are going to check that we get the same evaluated results
54
        // before and after mutation, then we need to preserve semantics.
55
        cfg!(feature = "wasmtime") && preserve_semantics,
56
    );
57
58
    let iterator = match wasm_mutate.run(&wasm) {
59
        Ok(iterator) => iterator,
60
        Err(e) => {
61
            log::warn!("Failed to mutate the Wasm: {}", e);
62
            return;
63
        }
64
    };
65
66
    // Note that on-by-default features in wasmparser are not disabled here if
67
    // the feature was disabled in `config` when the module was generated. For
68
    // example if the input module doesn't have simd then wasm-mutate may
69
    // produce a module that uses simd, which is ok and expected.
70
    //
71
    // Otherwise only forward some off-by-default features which are affected by
72
    // wasm-smith's generation of modules and wasm-mutate otherwise won't add
73
    // itself if it doesn't already exist.
74
    let mut features = WasmFeatures::default();
75
    features.relaxed_simd = config.relaxed_simd_enabled;
76
    features.multi_memory = config.max_memories > 1;
77
    features.memory64 = config.memory64_enabled;
78
    features.threads = config.threads_enabled;
79
80
    for (i, mutated_wasm) in iterator.take(10).enumerate() {
81
        let mutated_wasm = match mutated_wasm {
82
            Ok(w) => w,
83
            Err(e) => match e.kind() {
84
                wasm_mutate::ErrorKind::NoMutationsApplicable => continue,
85
                _ => panic!("Unexpected mutation failure: {}", e),
86
            },
87
        };
88
89
        // Increase ony once for the same input Wasm.
90
        if i == 0 {
91
            NUM_SUCCESSFUL_MUTATIONS.fetch_add(1, Ordering::Relaxed);
92
        }
93
94
        let validation_result =
95
            wasmparser::Validator::new_with_features(features).validate_all(&mutated_wasm);
96
97
        if log::log_enabled!(log::Level::Debug) {
98
            log::debug!("writing mutated Wasm to `mutated.wasm`");
99
            std::fs::write("mutated.wasm", &mutated_wasm)
100
                .expect("should write `mutated.wasm` okay");
101
            if let Ok(mutated_wat) = wasmprinter::print_bytes(&mutated_wasm) {
102
                log::debug!("writing mutated WAT to `mutated.wat`");
103
                std::fs::write("mutated.wat", &mutated_wat)
104
                    .expect("should write `mutated.wat` okay");
105
            }
106
        }
107
108
        validation_result.expect("`wasm-mutate` should always produce a valid Wasm file");
109
110
        #[cfg(feature = "wasmtime")]
111
        if preserve_semantics {
112
            eval::assert_same_evaluation(&wasm, &mutated_wasm);
113
        }
114
    }
115
});
116
117
#[cfg(feature = "wasmtime")]
118
#[path = "../../crates/fuzz-stats/src/lib.rs"]
119
pub mod fuzz_stats;
120
121
#[cfg(feature = "wasmtime")]
122
mod eval {
123
    use super::fuzz_stats::{dummy, limits::StoreLimits};
124
    use std::collections::hash_map::DefaultHasher;
125
    use std::hash::{Hash, Hasher};
126
    use wasmtime::{ResourceLimiter, Val};
127
128
    /// Compile, instantiate, and evaluate both the original and mutated Wasm.
129
    ///
130
    /// We should get identical results because we told `wasm-mutate` to preserve
131
    /// semantics.
132
    pub fn assert_same_evaluation(wasm: &[u8], mutated_wasm: &[u8]) {
133
        let mut config = wasmtime::Config::default();
134
        config.cranelift_nan_canonicalization(true);
135
        config.consume_fuel(true);
136
137
        let engine = wasmtime::Engine::new(&config).unwrap();
138
139
        let (orig_module, mutated_module) = match (
140
            wasmtime::Module::new(&engine, &wasm),
141
            wasmtime::Module::new(&engine, &mutated_wasm),
142
        ) {
143
            (Ok(o), Ok(m)) => (o, m),
144
            // Ideally we would assert that they both errored if either one did, but
145
            // it is possible that a mutation bumped some count above/below an
146
            // implementation limit.
147
            (_, _) => return,
148
        };
149
150
        let limits = StoreLimits {
151
            remaining_memory: 1 << 30,
152
            oom: false,
153
        };
154
        let mut orig_store = wasmtime::Store::new(&engine, limits.clone());
155
        let mut mutated_store = wasmtime::Store::new(&engine, limits);
156
        orig_store.limiter(|s| s as &mut dyn ResourceLimiter);
157
        mutated_store.limiter(|s| s as &mut dyn ResourceLimiter);
158
        let orig_imports = match dummy::dummy_imports(&mut orig_store, &orig_module) {
159
            Ok(imps) => imps,
160
            Err(_) => return,
161
        };
162
        let mutated_imports = match dummy::dummy_imports(&mut mutated_store, &mutated_module) {
163
            Ok(imps) => imps,
164
            Err(_) => return,
165
        };
166
167
        let (orig_instance, mutated_instance) = match (
168
            wasmtime::Instance::new(&mut orig_store, &orig_module, &orig_imports),
169
            wasmtime::Instance::new(&mut mutated_store, &mutated_module, &mutated_imports),
170
        ) {
171
            (Ok(x), Ok(y)) => (x, y),
172
            (_, _) => return,
173
        };
174
175
        assert_same_state(
176
            &orig_module,
177
            &mut orig_store,
178
            orig_instance,
179
            &mut mutated_store,
180
            mutated_instance,
181
        );
182
        assert_same_calls(
183
            &orig_module,
184
            &mut orig_store,
185
            orig_instance,
186
            &mut mutated_store,
187
            mutated_instance,
188
        );
189
        assert_same_state(
190
            &orig_module,
191
            &mut orig_store,
192
            orig_instance,
193
            &mut mutated_store,
194
            mutated_instance,
195
        );
196
    }
197
198
    fn assert_same_state(
199
        orig_module: &wasmtime::Module,
200
        orig_store: &mut wasmtime::Store<StoreLimits>,
201
        orig_instance: wasmtime::Instance,
202
        mutated_store: &mut wasmtime::Store<StoreLimits>,
203
        mutated_instance: wasmtime::Instance,
204
    ) {
205
        for export in orig_module.exports() {
206
            match export.ty() {
207
                wasmtime::ExternType::Global(_) => {
208
                    let orig = orig_instance
209
                        .get_export(&mut *orig_store, export.name())
210
                        .unwrap()
211
                        .into_global()
212
                        .unwrap()
213
                        .get(&mut *orig_store);
214
                    let mutated = mutated_instance
215
                        .get_export(&mut *mutated_store, export.name())
216
                        .unwrap()
217
                        .into_global()
218
                        .unwrap()
219
                        .get(&mut *mutated_store);
220
                    assert_val_eq(&orig, &mutated);
221
                }
222
                wasmtime::ExternType::Memory(_) => {
223
                    let orig = orig_instance
224
                        .get_export(&mut *orig_store, export.name())
225
                        .unwrap()
226
                        .into_memory()
227
                        .unwrap();
228
                    let mut h = DefaultHasher::default();
229
                    orig.data(&orig_store).hash(&mut h);
230
                    let orig = h.finish();
231
                    let mutated = mutated_instance
232
                        .get_export(&mut *mutated_store, export.name())
233
                        .unwrap()
234
                        .into_memory()
235
                        .unwrap();
236
                    let mut h = DefaultHasher::default();
237
                    mutated.data(&mutated_store).hash(&mut h);
238
                    let mutated = h.finish();
239
                    assert_eq!(orig, mutated, "original and mutated Wasm memories diverged");
240
                }
241
                _ => continue,
242
            }
243
        }
244
    }
245
246
    fn assert_same_calls(
247
        orig_module: &wasmtime::Module,
248
        orig_store: &mut wasmtime::Store<StoreLimits>,
249
        orig_instance: wasmtime::Instance,
250
        mutated_store: &mut wasmtime::Store<StoreLimits>,
251
        mutated_instance: wasmtime::Instance,
252
    ) {
253
        for export in orig_module.exports() {
254
            let func_ty = match export.ty() {
255
                wasmtime::ExternType::Func(func_ty) => func_ty,
256
                _ => continue,
257
            };
258
            let orig_func = orig_instance
259
                .get_func(&mut *orig_store, export.name())
260
                .unwrap();
261
            let mutated_func = mutated_instance
262
                .get_func(&mut *mutated_store, export.name())
263
                .unwrap();
264
            let args = dummy::dummy_values(func_ty.params());
265
            let mut orig_results = vec![Val::I32(0); func_ty.results().len()];
266
            let mut mutated_results = orig_results.clone();
267
            log::debug!("invoking `{}`", export.name());
268
            let prev_consumed = orig_store.fuel_consumed().unwrap();
269
            match (
270
                {
271
                    orig_store.add_fuel(1_000).unwrap();
272
                    orig_func.call(&mut *orig_store, &args, &mut orig_results)
273
                },
274
                {
275
                    let consumed = orig_store.fuel_consumed().unwrap() - prev_consumed;
276
                    // Add in some extra fuel to account for extra code that
277
                    // may be inserted by the mutation or to handle the case
278
                    // where the original execution trapped which may not have
279
                    // fully accounted for the last bit of fuel needed to reach
280
                    // the trap.
281
                    log::debug!("consumed {consumed} fuel");
282
                    mutated_store.add_fuel(consumed + 10).unwrap();
283
                    mutated_func.call(&mut *mutated_store, &args, &mut mutated_results)
284
                },
285
            ) {
286
                (Ok(()), Ok(())) => {
287
                    for (orig_val, mutated_val) in orig_results.iter().zip(mutated_results.iter()) {
288
                        assert_val_eq(orig_val, mutated_val);
289
                    }
290
                }
291
                (Err(orig), Err(mutated)) => {
292
                    log::debug!("original error {orig:?}");
293
                    log::debug!("mutated error {mutated:?}");
294
                    continue;
295
                }
296
                (orig, mutated) => panic!(
297
                    "mutated and original Wasm diverged: orig = {:?}; mutated = {:?}",
298
                    orig, mutated,
299
                ),
300
            }
301
        }
302
    }
303
304
    fn assert_val_eq(orig_val: &wasmtime::Val, mutated_val: &wasmtime::Val) {
305
        match (orig_val, mutated_val) {
306
            (wasmtime::Val::I32(o), wasmtime::Val::I32(m)) => assert_eq!(o, m),
307
            (wasmtime::Val::I64(o), wasmtime::Val::I64(m)) => assert_eq!(o, m),
308
            (wasmtime::Val::F32(o), wasmtime::Val::F32(m)) => {
309
                let o = f32::from_bits(*o);
310
                let m = f32::from_bits(*m);
311
                assert!(o == m || (o.is_nan() && m.is_nan()));
312
            }
313
            (wasmtime::Val::F64(o), wasmtime::Val::F64(m)) => {
314
                let o = f64::from_bits(*o);
315
                let m = f64::from_bits(*m);
316
                assert!(o == m || (o.is_nan() && m.is_nan()));
317
            }
318
            (wasmtime::Val::V128(o), wasmtime::Val::V128(m)) => {
319
                assert_eq!(o, m)
320
            }
321
            (wasmtime::Val::ExternRef(o), wasmtime::Val::ExternRef(m)) => {
322
                assert_eq!(o.is_none(), m.is_none())
323
            }
324
            (wasmtime::Val::FuncRef(o), wasmtime::Val::FuncRef(m)) => {
325
                assert_eq!(o.is_none(), m.is_none())
326
            }
327
            (o, m) => panic!(
328
                "mutated and original Wasm diverged: orig = {:?}; mutated = {:?}",
329
                o, m,
330
            ),
331
        }
332
    }
333
}