wasmi-labs / wasmi

WebAssembly (Wasm) interpreter.
https://wasmi-labs.github.io/wasmi/
Apache License 2.0
1.61k stars 286 forks source link

Add `Tinywasm` differential fuzzing oracle #1290

Open Robbepop opened 1 week ago

Robbepop commented 1 week ago

The Tinywasm Webassembly interpreter is interesting as a differential fuzzing oracle for Wasmi since it is a lightweight interpreter, supporting a similar Wasm feature set as Wasmi and is written in safe Rust entirely.

cc @explodingcamera Please feel free to share your thoughts if you support this. :)

Unfortunately it seems to be missing some important API, such as

As an example here is the oracle implementation for a legacy Wasmi version: https://github.com/wasmi-labs/wasmi/blob/main/crates/fuzz/src/oracle/wasmi_stack.rs

In detail it is required to fully implement these traits:

In a PR attempt I was already able to implement the FuzzValue and FuzzError conversions:

impl From<WasmValue> for FuzzVal {
    fn from(value: WasmValue) -> Self {
        match value {
            WasmValue::I32(value) => Self::I32(value),
            WasmValue::I64(value) => Self::I64(value),
            WasmValue::F32(value) => Self::F32(value.into()),
            WasmValue::F64(value) => Self::F64(value.into()),
            WasmValue::RefNull(ty) => match ty {
                ValType::RefFunc => Self::FuncRef { is_null: true },
                ValType::RefExtern => Self::ExternRef { is_null: true },
                _ => unimplemented!(),
            },
            WasmValue::RefFunc(_value) => Self::FuncRef { is_null: false },
            WasmValue::RefExtern(_value) => Self::ExternRef { is_null: false },
            WasmValue::V128(_value) => unimplemented!(),
        }
    }
}

impl From<FuzzVal> for WasmValue {
    fn from(value: FuzzVal) -> Self {
        match value {
            FuzzVal::I32(value) => Self::I32(value),
            FuzzVal::I64(value) => Self::I64(value),
            FuzzVal::F32(value) => Self::F32(value.into()),
            FuzzVal::F64(value) => Self::F64(value.into()),
            FuzzVal::FuncRef { is_null } => {
                assert!(is_null);
                Self::RefNull(ValType::RefFunc)
            }
            FuzzVal::ExternRef { is_null } => {
                assert!(is_null);
                Self::RefNull(ValType::RefExtern)
            }
        }
    }
}

impl From<Error> for FuzzError {
    fn from(error: Error) -> Self {
        let Error::Trap(trap) = error else {
            return FuzzError::Other;
        };
        let trap_code = match trap {
            Trap::Unreachable => crate::TrapCode::UnreachableCodeReached,
            Trap::MemoryOutOfBounds { .. } => crate::TrapCode::MemoryOutOfBounds,
            Trap::TableOutOfBounds { .. } => crate::TrapCode::TableOutOfBounds,
            Trap::DivisionByZero => crate::TrapCode::IntegerDivisionByZero,
            Trap::InvalidConversionToInt => crate::TrapCode::BadConversionToInteger,
            Trap::IntegerOverflow => crate::TrapCode::IntegerOverflow,
            Trap::CallStackOverflow => crate::TrapCode::StackOverflow,
            Trap::UndefinedElement { .. } => {
                crate::TrapCode::BadSignature // not sure if this error fits
            }
            Trap::UninitializedElement { .. } => {
                crate::TrapCode::BadSignature // not sure if this error fits
            }
            Trap::IndirectCallTypeMismatch { .. } => crate::TrapCode::IndirectCallToNull,
        };
        FuzzError::Trap(trap_code)
    }
}
explodingcamera commented 1 week ago

Hi, sounds interesting! I'll be sure to add those APIs to my todo list for the next release, the public API has been a bit neglected.

Robbepop commented 1 week ago

Hi, sounds interesting! I'll be sure to add those APIs to my todo list for the next release, the public API has been a bit neglected.

Great! Sounds good. I'd be happy to be updated here once the new update lands. :)