Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ look at the sub-crates:
* [**`wast`**](crates/wast) - like `wat`, except provides an AST
* [**`wasmprinter`**](crates/wasmprinter) - prints WebAssembly binaries in their
string form
* [**`wasm-mutate`**](crates/wasm-mutate) - a WebAssembly test case mutator
* [**`wasm-shrink`**](crates/wasm-shrink) - a WebAssembly test case shrinker
* [**`wasm-smith`**](crates/wasm-smith) - a WebAssembly test case generator
* [**`wasm-encoder`**](crates/wasm-encoder) - a crate to generate a binary
WebAssembly module
Expand Down
4 changes: 2 additions & 2 deletions crates/wasm-mutate-stats/src/bin/wasm-mutate-stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,8 @@ impl State {
// First stage, generate and return the mutated
let it = match wasmmutate.run(&wasmcp) {
Ok(it) => it,
Err(e) => match e {
wasm_mutate::Error::NoMutationsApplicable => {
Err(e) => match e.kind() {
wasm_mutate::ErrorKind::NoMutationsApplicable => {
Box::new(std::iter::once(Ok(wasm.clone())))
}
_ => {
Expand Down
96 changes: 74 additions & 22 deletions crates/wasm-mutate/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,83 @@
use wasm_encoder::ValType;
use wasmparser::{GlobalType, Type};

/// An error encountered when choosing or applying a Wasm mutation.
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// The input Wasm module did not parse or validate okay.
#[error("Failed to parse or validate the input Wasm module.")]
#[error(transparent)]
pub struct Error {
kind: Box<ErrorKind>,
}

impl Error {
/// Construct a new `Error` from an `ErrorKind`.
pub fn new(kind: ErrorKind) -> Self {
kind.into()
}

/// Construct a new parse error.
pub fn parse(err: wasmparser::BinaryReaderError) -> Self {
err.into()
}

/// Construct a "no mutations applicable" error.
pub fn no_mutations_applicable() -> Self {
ErrorKind::NoMutationsApplicable.into()
}

/// Construct an "out of fuel" error.
pub fn out_of_fuel() -> Self {
ErrorKind::OutOfFuel.into()
}

/// Construct an unsupported error.
pub fn unsupported(msg: impl Into<String>) -> Self {
ErrorKind::Unsupported(msg.into()).into()
}

/// Construct another kind of `Error`.
pub fn other(err: impl Into<String>) -> Self {
ErrorKind::Other(err.into()).into()
}

/// Get the kind of error that this is.
pub fn kind(&self) -> &ErrorKind {
&*self.kind
}
}

impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Error {
kind: Box::new(kind),
}
}
}

impl From<wasmparser::BinaryReaderError> for Error {
fn from(e: wasmparser::BinaryReaderError) -> Self {
ErrorKind::Parse(e).into()
}
}

/// The kind of error.
#[derive(thiserror::Error, Debug)]
pub enum ErrorKind {
/// Failed to parse the input Wasm module.
#[error("Failed to parse the input Wasm module.")]
Parse(#[from] wasmparser::BinaryReaderError),

/// None of the available mutators are applicable to the input Wasm module
#[error("There are not applicable mutations for this module.")]
#[error("There are not applicable mutations for the input Wasm module.")]
NoMutationsApplicable,
/// There is a type/operator that wasm-mutate cannot process
#[error("Unsupported mapping.")]
UnsupportedType(EitherType),
/// Ast parsing error for code motion mutators
#[error("Invalid Ast parsing for code motion")]
InvalidAstOperation(String),
}

#[derive(Debug)]
pub enum EitherType {
Type(Type),
TypeDef(String),
Operator(String),
ValType(ValType),
EggError(String),
GlobalType(GlobalType),
/// Ran out of fuel before a mutation could be applied succesfully.
#[error("Out of fuel")]
OutOfFuel,

/// The Wasm is using an unsupported feature.
#[error("Unsupported: {0}")]
Unsupported(String),

/// Another error.
#[error("{0}")]
Other(String),
}

/// A `Result` type that is either `Ok(T)` or `Err(wasm_mutate::Error)`.
Expand Down
29 changes: 16 additions & 13 deletions crates/wasm-mutate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,30 @@
//! Wasm parser, validator, compiler, or any other Wasm-consuming
//! tool. `wasm-mutate` can serve as a custom mutator for mutation-based
//! fuzzing.

#![cfg_attr(not(feature = "structopt"), deny(missing_docs))]

mod error;
mod info;
mod module;
mod mutators;

pub use error::*;

use crate::mutators::{
codemotion::CodemotionMutator, data::RemoveDataSegment, elems::RemoveElemSegment,
function_body_unreachable::FunctionBodyUnreachable, peephole::PeepholeMutator,
remove_export::RemoveExportMutator, rename_export::RenameExportMutator,
snip_function::SnipMutator,
codemotion::CodemotionMutator, custom::RemoveCustomSection, data::RemoveDataSegment,
elems::RemoveElemSegment, function_body_unreachable::FunctionBodyUnreachable,
peephole::PeepholeMutator, remove_export::RemoveExportMutator,
rename_export::RenameExportMutator, snip_function::SnipMutator,
};
use info::ModuleInfo;
use mutators::Mutator;

use rand::{rngs::SmallRng, Rng, SeedableRng};
use std::{cell::Cell, sync::Arc};

#[cfg(feature = "structopt")]
use structopt::StructOpt;

pub use error::{Error, Result};

macro_rules! define_mutators {
(@count) => {0};
(@count $first: expr , $( $tail: expr ,) *) => { 1 + define_mutators!(@count $($tail , )*) };
Expand All @@ -45,7 +47,7 @@ macro_rules! define_mutators {
return Ok(Box::new(iter.into_iter().map(|r| r.map(|m| m.finish()))))
}
Err(e) => {
log::info!("mutator {} failed: {}; will try again", m.name(), e);
log::debug!("mutator {} failed: {}; will try again", m.name(), e);
return Err(e);
}
}
Expand All @@ -60,7 +62,7 @@ macro_rules! define_mutators {
return Ok(Box::new(iter.into_iter().map(|r| r.map(|m| m.finish()))))
}
Err(e) => {
log::info!("mutator {} failed: {}; will try again", m.name(), e);
log::debug!("mutator {} failed: {}; will try again", m.name(), e);
return Err(e);
}
}
Expand All @@ -76,7 +78,7 @@ macro_rules! define_mutators {
return Ok(Box::new(iter.into_iter().map(|r| r.map(|m| m.finish()))))
}
Err(e) => {
log::info!("mutator {} failed: {}; will try again", m.name(), e);
log::debug!("mutator {} failed: {}; will try again", m.name(), e);
return Err(e);
}
}
Expand Down Expand Up @@ -241,8 +243,8 @@ impl<'wasm> WasmMutate<'wasm> {

pub(crate) fn consume_fuel(&self, qt: u64) -> Result<()> {
if qt > self.fuel.get() {
log::info!("Resource limits reached!");
return Err(crate::Error::NoMutationsApplicable); // Replace by a TimeoutError type
log::info!("Out of fuel");
return Err(Error::out_of_fuel());
}
self.fuel.set(self.fuel.get() - qt);
Ok(())
Expand Down Expand Up @@ -274,10 +276,11 @@ impl<'wasm> WasmMutate<'wasm> {
FunctionBodyUnreachable,
RemoveElemSegment,
RemoveDataSegment,
RemoveCustomSection,
)
);

Err(crate::Error::NoMutationsApplicable)
Err(Error::no_mutations_applicable())
}

pub(crate) fn rng(&mut self) -> &mut SmallRng {
Expand Down
68 changes: 29 additions & 39 deletions crates/wasm-mutate/src/module.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use crate::{Error, Result};
use std::convert::TryFrom;

use wasm_encoder::{BlockType, ValType};
use wasmparser::{Type, TypeDef, TypeOrFuncType};

use crate::error::EitherType;

#[derive(Debug, Clone, PartialEq)]
pub enum PrimitiveTypeInfo {
I32,
Expand All @@ -31,51 +29,46 @@ pub enum TypeInfo {
// TODO: module linking support will require instance and module types.
}

impl TryFrom<Type> for PrimitiveTypeInfo {
type Error = super::Error;

fn try_from(value: Type) -> Result<Self, Self::Error> {
impl From<Type> for PrimitiveTypeInfo {
fn from(value: Type) -> Self {
match value {
Type::I32 => Ok(PrimitiveTypeInfo::I32),
Type::I64 => Ok(PrimitiveTypeInfo::I64),
Type::F32 => Ok(PrimitiveTypeInfo::F32),
Type::F64 => Ok(PrimitiveTypeInfo::F64),
Type::V128 => Ok(PrimitiveTypeInfo::V128),
Type::FuncRef => Ok(PrimitiveTypeInfo::FuncRef),
Type::ExternRef => Ok(PrimitiveTypeInfo::ExternRef),
Type::EmptyBlockType => Ok(PrimitiveTypeInfo::Empty),
Type::ExnRef => Ok(PrimitiveTypeInfo::ExnRef),
Type::Func => Ok(PrimitiveTypeInfo::Func),
Type::I32 => PrimitiveTypeInfo::I32,
Type::I64 => PrimitiveTypeInfo::I64,
Type::F32 => PrimitiveTypeInfo::F32,
Type::F64 => PrimitiveTypeInfo::F64,
Type::V128 => PrimitiveTypeInfo::V128,
Type::FuncRef => PrimitiveTypeInfo::FuncRef,
Type::ExternRef => PrimitiveTypeInfo::ExternRef,
Type::EmptyBlockType => PrimitiveTypeInfo::Empty,
Type::ExnRef => PrimitiveTypeInfo::ExnRef,
Type::Func => PrimitiveTypeInfo::Func,
}
}
}

impl TryFrom<TypeDef<'_>> for TypeInfo {
type Error = super::Error;
type Error = Error;

fn try_from(value: TypeDef<'_>) -> Result<Self, Self::Error> {
fn try_from(value: TypeDef<'_>) -> Result<Self> {
match value {
TypeDef::Func(ft) => Ok(TypeInfo::Func(FuncInfo {
params: ft
.params
.iter()
.map(|&t| PrimitiveTypeInfo::try_from(t))
.collect::<Result<Vec<_>, _>>()?,
.map(|&t| PrimitiveTypeInfo::from(t))
.collect(),
returns: ft
.returns
.iter()
.map(|&t| PrimitiveTypeInfo::try_from(t))
.collect::<Result<Vec<_>, _>>()?,
.map(|&t| PrimitiveTypeInfo::from(t))
.collect(),
})),
_ => Err(super::Error::UnsupportedType(EitherType::TypeDef(format!(
"{:?}",
value
)))),
_ => Err(Error::unsupported(format!("{:?}", value))),
}
}
}

pub fn map_type(tpe: Type) -> super::Result<ValType> {
pub fn map_type(tpe: Type) -> Result<ValType> {
match tpe {
Type::I32 => Ok(ValType::I32),
Type::I64 => Ok(ValType::I64),
Expand All @@ -84,20 +77,17 @@ pub fn map_type(tpe: Type) -> super::Result<ValType> {
Type::V128 => Ok(ValType::V128),
Type::FuncRef => Ok(ValType::FuncRef),
Type::ExternRef => Ok(ValType::ExternRef),
_ => Err(super::Error::UnsupportedType(EitherType::Type(tpe))),
_ => Err(Error::unsupported(format!(
"{:?} is not supported in `wasm-encoder`",
tpe
))),
}
}

pub fn map_block_type(ty: TypeOrFuncType) -> super::Result<BlockType> {
pub fn map_block_type(ty: TypeOrFuncType) -> Result<BlockType> {
match ty {
TypeOrFuncType::Type(ty) => match ty {
Type::I32 => Ok(BlockType::Result(ValType::I32)),
Type::I64 => Ok(BlockType::Result(ValType::I64)),
Type::F32 => Ok(BlockType::Result(ValType::F32)),
Type::F64 => Ok(BlockType::Result(ValType::F64)),
Type::EmptyBlockType => Ok(BlockType::Empty),
_ => Err(super::Error::NoMutationsApplicable),
},
TypeOrFuncType::FuncType(_) => Err(super::Error::NoMutationsApplicable),
TypeOrFuncType::FuncType(f) => Ok(BlockType::FunctionType(f)),
TypeOrFuncType::Type(Type::EmptyBlockType) => Ok(BlockType::Empty),
TypeOrFuncType::Type(ty) => Ok(BlockType::Result(map_type(ty)?)),
}
}
Loading