54 releases (6 breaking)

new 0.205.0 Apr 18, 2024
0.203.0 Apr 12, 2024
0.202.0 Mar 26, 2024
0.1.44 Nov 29, 2023
0.1.3 Feb 17, 2022

#1304 in WebAssembly

Download history 45/week @ 2023-12-25 79/week @ 2024-01-01 173/week @ 2024-01-08 150/week @ 2024-01-15 226/week @ 2024-01-22 256/week @ 2024-01-29 215/week @ 2024-02-05 406/week @ 2024-02-12 203/week @ 2024-02-19 395/week @ 2024-02-26 236/week @ 2024-03-04 326/week @ 2024-03-11 335/week @ 2024-03-18 359/week @ 2024-03-25 411/week @ 2024-04-01 94/week @ 2024-04-08

1,250 downloads per month
Used in wasm-tools

Apache-2.0 WITH LLVM-exception

1.5MB
31K SLoC

wasm-shrink

A WebAssembly test case shrinker.

About

wasm-shrink is a test case shrinker for WebAssembly. It shrinks a Wasm file while preserving an interesting property (such as triggering a bug in your Wasm compiler).

Usage

Install

$ cargo install wasm-tools

Writing a Predicate Script

The predicate script tells wasm-shrink whether a Wasm file is "interesting" or not, i.e. whether the Wasm file triggers the bug you are trying to isolate.

The interface between wasm-shrink and a predicate script is simple:

  • The predicate script is given a Wasm file as its first and only argument.

  • The predicate script must exit with a zero status code if the Wasm file is interesting and a non-zero status code otherwise.

The predicate script must not depend on the current working directory nor that only one predicate script process is running at any given time.

Here is an example predicate script that could be used to track down a panic in Wasmtime that includes the panic message "assertion failed: invalid stack map":

#!/usr/bin/env bash

# Exit the script if any subcommand fails.
set -e

# The Wasm file is given as the first and only argument to the script.
WASM=$1

# Run the Wasm in Wasmtime and `grep` for our target bug's panic
# message.
wasmtime run $WASM 2>&1 | grep --quiet 'assertion failed: invalid stack map'

Note that passing grep the --quiet flag makes it avoid its usual match-printing behavior and exit with status code zero if there is any match and non-zero if there is not any match. This is useful for predicate scripts.

Run

To run wasm-tools shrink pass it the predicate and the initial test case:

$ wasm-tools shrink predicate.sh test-case.wasm -o shrunken.wasm

The shrunken Wasm file will be written to shrunken.wasm in this case, but if the -o flag is not given an output name is generated based on the initial test case's name.

You can see all options by passing --help:

$ wasm-tools shrink --help

Embed as a Library

wasm-shrink is not only a command-line tool, it also defines a library to allow programmatic usage.

First, add a dependency to your Cargo.toml:

$ cargo add wasm-shrink

Then use the wasm_shrink::WasmShrink builder to configure and run a shrinking task:

use wasm_shrink::WasmShrink;

// Get the Wasm you want to shrink from somewhere.
let my_input_wasm: Vec<u8> = todo!();

// Configure the shrinking task.
let shrink = WasmShrink::default()
    // Give up on shrinking after 999 failed attempts to shrink a given
    // Wasm test case any further.
    .attempts(999);

// Run the configured shrinking task.
let info = shrink.run(
    my_input_wasm,

    // Predicate.
    &mut |wasm| {
        let is_interesting: bool = todo!(
            "check for whether the given Wasm is interesting"
        );
        Ok(is_interesting)
    },

    // Callback called each time we find a new smallest interesting
    // Wasm.
    &mut |new_smallest| {
        // Optionally do something with the new smallest Wasm.
        Ok(())
    },
)?;

// Get the shrunken Wasm and other information about the completed shrink
// task from the returned `ShrinkInfo`.
let shrunken_wasm = info.output;

For more details, see the documentation on docs.rs.

Dependencies

~5MB
~106K SLoC