56 releases (8 breaking)

new 0.206.0 Apr 25, 2024
0.204.0 Apr 16, 2024
0.202.0 Mar 26, 2024
0.2.43 Nov 29, 2023
0.2.1 Feb 17, 2022

#1189 in WebAssembly

Download history 1662/week @ 2024-01-04 2193/week @ 2024-01-11 2578/week @ 2024-01-18 3129/week @ 2024-01-25 3127/week @ 2024-02-01 3505/week @ 2024-02-08 3351/week @ 2024-02-15 3949/week @ 2024-02-22 3484/week @ 2024-02-29 4081/week @ 2024-03-07 4303/week @ 2024-03-14 3854/week @ 2024-03-21 3732/week @ 2024-03-28 3258/week @ 2024-04-04 4733/week @ 2024-04-11 5324/week @ 2024-04-18

17,546 downloads per month
Used in 2 crates

Apache-2.0 WITH LLVM-exception

2MB
38K SLoC

wasm-mutate

A Bytecode Alliance project

wasm-mutate is a new tool for fuzzing Wasm compilers, runtimes, validators, and other Wasm-consuming programs.

Crates.io version Download docs.rs docs

Usage

Add wasm-mutate to your Cargo.toml:

$ cargo add wasm-mutate

You can also mutate a WebAssembly binary by using the cli tool:

wasm-tools mutate original.wasm --seed 0 -o out.wasm --preserve-semantics

Features

  • semantically equivalent transformations: wasm-mutate has the ability to only apply semantics-preserving changes to the input Wasm module. When it is used in this mode, the mutated Wasm computes identical results when given the same inputs as the original Wasm module.

  • determinism: wasm-mutate is deterministic, i.e., given the same input Wasm module and the same seed, it always produces the same mutated output Wasm module.

  • libfuzzer integration: wasm-mutate integrates well with mutation-based fuzzers like libFuzzer. It reuses the fuzzer's raw input strings. wasm-mutate works with the LLVMFuzzerCustomMutator hook and the libfuzzer_sys::fuzz_mutator! macro.

    Example

    #![no_main]
    
    use libfuzzer_sys::{fuzz_mutator, fuzz_target};
    use std::io::{BufRead, Read, Write};
    use wasmparser::WasmFeatures;
    
    fuzz_target!(|bytes: &[u8]| {
        // Initialize the Wasm for example
    });
    
    fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| {
        // Generate a random Wasm module with `wasm-smith` as well as a RNG seed for
        let wasm = &data[..size];
        let features = WasmFeatures::default();
        let mut validator = wasmparser::Validator::new();
        validator.wasm_features(features);
        let validation_result = validator.validate_all(&wasm);
    
        // Mutate the data if its a valid Wasm file, otherwise, create a random one
        let wasm = if validation_result.is_ok() {
            wasm.to_vec()
        } else {
            let (w, _) = match wasm_tools_fuzz::generate_valid_module_from_seed(seed, |config, u| {
                config.exceptions_enabled = false;
                config.simd_enabled = false;
                config.reference_types_enabled = false;
                config.memory64_enabled = false;
                config.max_memories = 1;
                Ok(())
            }) {
                Ok(m) => m,
                Err(_) => {
                    return size;
                }
            };
            w
        };
    
        let mutated_wasm = wasm_mutate::WasmMutate::default()
            .seed(seed.into())
            .fuel(1000)
            .preserve_semantics(true)
            .run(&wasm);
    
        let mutated_wasm = match mutated_wasm {
            Ok(w) => w,
            Err(_) => wasm,
        };
    
        // The mutated Wasm should still be valid, since the input Wasm was valid.
        let newsize = mutated_wasm.len();
        data[..newsize].copy_from_slice(&mutated_wasm[..newsize]);
        newsize
    });
    
    
    
  • test case reduction (WIP): wasm-mutate can have the ability to restrict mutations to only those that shrink the size of the Wasm module. If it is used in this mode, wasm-mutate essentially becomes a Wasm test-case reducer. We are currently working to provide a prototype of this feature as a separate binary. The following pseudo-Rust provides the general picture of it as an standard hill-climbing algorithm.

      let wasmmutate = wasm_mutate::WasmMutate::default()
              .seed(seed)
              .fuel(1000)
              .preserve_semantics(true)
              .reduce(true);
    
      while MAX_ITERATIONS > 0 {
          let new_wasm = wasmmutate.run(&wasm);
          wasm = if check_equivalence(new_wasm, wasm) {
            wasm
          } else{
            panic!("No valid transformations")
          }
          MAX_ITERATIONS -= 1;
      }
    
      return wasm
    
    

License

This project is licensed under the Apache 2.0 license with the LLVM exception. See LICENSE for more details.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

Special contribution

  • Javier Cabrera Arteaga (Phd. student at KTH)

Dependencies

~2.6–3.5MB
~66K SLoC