Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Module::validate API from Wasmtime #840

Merged
merged 4 commits into from
Dec 8, 2023
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: 0 additions & 2 deletions crates/wasmi/src/engine/translator/tests/regression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ fn fuzz_regression_4() {
fn fuzz_regression_5() {
let wat = include_str!("fuzz_5.wat");
let wasm = wat2wasm(wat);
_ = wasmparser::validate(&wasm[..]).unwrap();
TranslationTest::new(wasm)
.expect_func_instrs([
Instruction::call_internal(
Expand All @@ -124,7 +123,6 @@ fn fuzz_regression_5() {
fn fuzz_regression_6() {
let wat = include_str!("fuzz_6.wat");
let wasm = wat2wasm(wat);
_ = wasmparser::validate(&wasm[..]).unwrap();
TranslationTest::new(wasm)
.expect_func_instrs([
Instruction::branch_i32_eq_imm(Register::from_i16(0), 0, BranchOffset16::from(4)),
Expand Down
24 changes: 21 additions & 3 deletions crates/wasmi/src/module/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,36 @@ pub enum ModuleError {
Parser(ParserError),
/// Encountered when there is a Wasm to `wasmi` translation error.
Translation(TranslationError),
/// Encountered unsupported Wasm feature usage.
Unsupported(UnsupportedFeature),
}

/// An unsupported Wasm feature.
#[derive(Debug)]
pub enum UnsupportedFeature {
/// The Wasm component model.
ComponentModel,
}

impl Display for ModuleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ModuleError::Read(error) => Display::fmt(error, f),
ModuleError::Parser(error) => Display::fmt(error, f),
ModuleError::Translation(error) => Display::fmt(error, f),
Self::Read(error) => Display::fmt(error, f),
Self::Parser(error) => Display::fmt(error, f),
Self::Translation(error) => Display::fmt(error, f),
Self::Unsupported(feature) => {
write!(f, "encountered unsupported Wasm feature: {feature:?}")
}
}
}
}

impl From<UnsupportedFeature> for ModuleError {
fn from(feature: UnsupportedFeature) -> Self {
Self::Unsupported(feature)
}
}

impl From<ReadError> for ModuleError {
fn from(error: ReadError) -> Self {
Self::Read(error)
Expand Down
47 changes: 47 additions & 0 deletions crates/wasmi/src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub(crate) use self::{
};
use crate::{
engine::{CompiledFunc, DedupFuncType},
module::error::UnsupportedFeature,
Engine,
Error,
ExternType,
Expand All @@ -48,6 +49,7 @@ use crate::{
};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc};
use core::{iter, slice::Iter as SliceIter};
use wasmparser::{FuncValidatorAllocations, Parser, ValidPayload, Validator};

/// A parsed and validated WebAssembly module.
#[derive(Debug)]
Expand Down Expand Up @@ -187,6 +189,51 @@ impl Module {
&self.engine
}

/// Validates `wasm` as a WebAssembly binary given the configuration (via [`Config`]) in `engine`.
///
/// This function performs Wasm validation of the binary input WebAssembly module and
/// returns either `Ok`` or `Err`` depending on the results of the validation.
/// The [`Config`] of the `engine` is used for Wasm validation which indicates which WebAssembly
/// features are valid and invalid for the validation.
///
/// # Note
///
/// - The input `wasm` must be in binary form, the text format is not accepted by this function.
/// - This will only validate the `wasm` but not try to translate it. Therefore `Module::new`
/// might still fail if translation of the Wasm binary input fails to translate via the `wasmi`
/// [`Engine`].
/// - Validation automatically happens as part of [`Module::new`].
///
/// # Errors
///
/// If Wasm validation for `wasm` fails for the given [`Config`] provided via `engine`.
///
/// [`Config`]: crate::Config
pub fn validate(&self, engine: &Engine, wasm: &[u8]) -> Result<(), Error> {
let mut validator = Validator::new_with_features(engine.config().wasm_features());
for payload in Parser::new(0).parse_all(wasm) {
let payload = payload.map_err(ModuleError::from)?;
if let ValidPayload::Func(func_to_validate, func_body) =
validator.payload(&payload).map_err(ModuleError::from)?
{
func_to_validate
.into_validator(FuncValidatorAllocations::default())
.validate(&func_body)
.map_err(ModuleError::from)?;
}
if let wasmparser::Payload::Version {
encoding: wasmparser::Encoding::Component,
..
} = &payload
{
return Err(Error::from(ModuleError::from(
UnsupportedFeature::ComponentModel,
)));
}
}
Ok(())
}

/// Creates a new [`Module`] from the [`ModuleBuilder`].
fn from_builder(builder: ModuleBuilder) -> Self {
Self {
Expand Down
8 changes: 3 additions & 5 deletions crates/wasmi/src/module/parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{
compile::{translate, translate_unchecked},
error::UnsupportedFeature,
export::ExternIdx,
global::Global,
import::{FuncTypeIdx, Import},
Expand Down Expand Up @@ -572,12 +573,9 @@ impl<'engine> ModuleParser<'engine> {
/// Process the entries for the Wasm component model proposal.
fn process_unsupported_component_model(
&mut self,
range: Range<usize>,
_range: Range<usize>,
) -> Result<(), ModuleError> {
panic!(
"wasmi does not support the `component-model` Wasm proposal: bytes[{}..{}]",
range.start, range.end
)
Err(ModuleError::from(UnsupportedFeature::ComponentModel))
}

/// Process an unknown Wasm module section.
Expand Down
Loading