* Bring back `Module::deserialize` I thought I was being clever suggesting that `Module::deserialize` was removed from #2791 by funneling all module constructors into `Module::new`. As our studious fuzzers have found, though, this means that `Module::new` is not safe currently to pass arbitrary user-defined input into. Now one might pretty reasonable expect to be able to do that, however, being a WebAssembly engine and all. This PR as a result separates the `deserialize` part of `Module::new` back into `Module::deserialize`. This means that binary blobs created with `Module::serialize` and `Engine::precompile_module` will need to be passed to `Module::deserialize` to "rehydrate" them back into a `Module`. This restores the property that it should be safe to pass arbitrary input to `Module::new` since it's always expected to be a wasm module. This also means that fuzzing will no longer attempt to fuzz `Module::deserialize` which isn't something we want to do anyway. * Fix an example * Mark `Module::deserialize` as `unsafe`
298 lines
8.7 KiB
Rust
298 lines
8.7 KiB
Rust
use anyhow::Result;
|
|
use wasmtime::*;
|
|
|
|
fn engine() -> Engine {
|
|
let mut config = Config::new();
|
|
config.wasm_module_linking(true);
|
|
Engine::new(&config).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn compile() -> Result<()> {
|
|
let engine = engine();
|
|
Module::new(&engine, "(module (module))")?;
|
|
Module::new(&engine, "(module (module) (module))")?;
|
|
Module::new(&engine, "(module (module (module)))")?;
|
|
Module::new(
|
|
&engine,
|
|
"
|
|
(module
|
|
(func)
|
|
(module (func))
|
|
(module (func))
|
|
)
|
|
",
|
|
)?;
|
|
let m = Module::new(
|
|
&engine,
|
|
"
|
|
(module
|
|
(global i32 (i32.const 0))
|
|
(func)
|
|
(module (memory 1) (func))
|
|
(module (memory 2) (func))
|
|
(module (table 2 funcref) (func))
|
|
(module (global i64 (i64.const 0)) (func))
|
|
)
|
|
",
|
|
)?;
|
|
assert_eq!(m.imports().len(), 0);
|
|
assert_eq!(m.exports().len(), 0);
|
|
let bytes = m.serialize()?;
|
|
unsafe {
|
|
Module::deserialize(&engine, &bytes)?;
|
|
}
|
|
assert_eq!(m.imports().len(), 0);
|
|
assert_eq!(m.exports().len(), 0);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn types() -> Result<()> {
|
|
let engine = engine();
|
|
Module::new(&engine, "(module (type (module)))")?;
|
|
Module::new(&engine, "(module (type (instance)))")?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn imports_exports() -> Result<()> {
|
|
let engine = engine();
|
|
|
|
// empty module type
|
|
let module = Module::new(&engine, "(module (module (export \"\")))")?;
|
|
let mut e = module.exports();
|
|
assert_eq!(e.len(), 1);
|
|
let export = e.next().unwrap();
|
|
assert_eq!(export.name(), "");
|
|
let module_ty = match export.ty() {
|
|
ExternType::Module(m) => m,
|
|
_ => panic!("unexpected type"),
|
|
};
|
|
assert_eq!(module_ty.imports().len(), 0);
|
|
assert_eq!(module_ty.exports().len(), 0);
|
|
|
|
// empty instance type
|
|
let module = Module::new(
|
|
&engine,
|
|
"
|
|
(module
|
|
(module)
|
|
(instance (export \"\") (instantiate 0)))
|
|
",
|
|
)?;
|
|
let mut e = module.exports();
|
|
assert_eq!(e.len(), 1);
|
|
let export = e.next().unwrap();
|
|
assert_eq!(export.name(), "");
|
|
let instance_ty = match export.ty() {
|
|
ExternType::Instance(i) => i,
|
|
_ => panic!("unexpected type"),
|
|
};
|
|
assert_eq!(instance_ty.exports().len(), 0);
|
|
|
|
// full module type
|
|
let module = Module::new(
|
|
&engine,
|
|
"
|
|
(module
|
|
(import \"\" \"a\" (module
|
|
(import \"a\" (func))
|
|
(export \"\" (global i32))
|
|
))
|
|
)
|
|
",
|
|
)?;
|
|
let mut i = module.imports();
|
|
assert_eq!(i.len(), 1);
|
|
let import = i.next().unwrap();
|
|
assert_eq!(import.module(), "");
|
|
assert_eq!(import.name(), None);
|
|
let instance_ty = match import.ty() {
|
|
ExternType::Instance(t) => t,
|
|
_ => panic!("unexpected type"),
|
|
};
|
|
assert_eq!(instance_ty.exports().len(), 1);
|
|
let module_ty = match instance_ty.exports().next().unwrap().ty() {
|
|
ExternType::Module(m) => m,
|
|
_ => panic!("unexpected type"),
|
|
};
|
|
assert_eq!(module_ty.imports().len(), 1);
|
|
assert_eq!(module_ty.exports().len(), 1);
|
|
let import = module_ty.imports().next().unwrap();
|
|
assert_eq!(import.module(), "a");
|
|
assert_eq!(import.name(), None);
|
|
match import.ty() {
|
|
ExternType::Func(f) => {
|
|
assert_eq!(f.results().len(), 0);
|
|
assert_eq!(f.params().len(), 0);
|
|
}
|
|
_ => panic!("unexpected type"),
|
|
}
|
|
let export = module_ty.exports().next().unwrap();
|
|
assert_eq!(export.name(), "");
|
|
match export.ty() {
|
|
ExternType::Global(g) => {
|
|
assert_eq!(*g.content(), ValType::I32);
|
|
assert_eq!(g.mutability(), Mutability::Const);
|
|
}
|
|
_ => panic!("unexpected type"),
|
|
}
|
|
|
|
// full instance type
|
|
let module = Module::new(
|
|
&engine,
|
|
"
|
|
(module
|
|
(import \"\" \"b\" (instance
|
|
(export \"m\" (memory 1))
|
|
(export \"t\" (table 1 funcref))
|
|
))
|
|
)
|
|
",
|
|
)?;
|
|
let mut i = module.imports();
|
|
assert_eq!(i.len(), 1);
|
|
let import = i.next().unwrap();
|
|
assert_eq!(import.module(), "");
|
|
assert_eq!(import.name(), None);
|
|
let instance_ty = match import.ty() {
|
|
ExternType::Instance(t) => t,
|
|
_ => panic!("unexpected type"),
|
|
};
|
|
assert_eq!(instance_ty.exports().len(), 1);
|
|
let instance_ty = match instance_ty.exports().next().unwrap().ty() {
|
|
ExternType::Instance(m) => m,
|
|
_ => panic!("unexpected type"),
|
|
};
|
|
assert_eq!(instance_ty.exports().len(), 2);
|
|
let mem_export = instance_ty.exports().nth(0).unwrap();
|
|
assert_eq!(mem_export.name(), "m");
|
|
match mem_export.ty() {
|
|
ExternType::Memory(m) => {
|
|
assert_eq!(m.limits().min(), 1);
|
|
assert_eq!(m.limits().max(), None);
|
|
}
|
|
_ => panic!("unexpected type"),
|
|
}
|
|
let table_export = instance_ty.exports().nth(1).unwrap();
|
|
assert_eq!(table_export.name(), "t");
|
|
match table_export.ty() {
|
|
ExternType::Table(t) => {
|
|
assert_eq!(t.limits().min(), 1);
|
|
assert_eq!(t.limits().max(), None);
|
|
assert_eq!(*t.element(), ValType::FuncRef);
|
|
}
|
|
_ => panic!("unexpected type"),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn limit_instances() -> Result<()> {
|
|
let mut config = Config::new();
|
|
config.wasm_module_linking(true);
|
|
let engine = Engine::new(&config)?;
|
|
let module = Module::new(
|
|
&engine,
|
|
r#"
|
|
(module $PARENT
|
|
(module $m0)
|
|
(module $m1
|
|
(instance (instantiate (module outer $PARENT $m0)))
|
|
(instance (instantiate (module outer $PARENT $m0))))
|
|
(module $m2
|
|
(instance (instantiate (module outer $PARENT $m1)))
|
|
(instance (instantiate (module outer $PARENT $m1))))
|
|
(module $m3
|
|
(instance (instantiate (module outer $PARENT $m2)))
|
|
(instance (instantiate (module outer $PARENT $m2))))
|
|
(module $m4
|
|
(instance (instantiate (module outer $PARENT $m3)))
|
|
(instance (instantiate (module outer $PARENT $m3))))
|
|
(module $m5
|
|
(instance (instantiate (module outer $PARENT $m4)))
|
|
(instance (instantiate (module outer $PARENT $m4))))
|
|
(instance (instantiate $m5))
|
|
)
|
|
"#,
|
|
)?;
|
|
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().instances(10).build());
|
|
let err = Instance::new(&store, &module, &[]).err().unwrap();
|
|
assert!(
|
|
err.to_string().contains("resource limit exceeded"),
|
|
"bad error: {}",
|
|
err
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn limit_memories() -> Result<()> {
|
|
let mut config = Config::new();
|
|
config.wasm_module_linking(true);
|
|
config.wasm_multi_memory(true);
|
|
let engine = Engine::new(&config)?;
|
|
let module = Module::new(
|
|
&engine,
|
|
r#"
|
|
(module
|
|
(module $m0
|
|
(memory 1 1)
|
|
(memory 1 1)
|
|
(memory 1 1)
|
|
(memory 1 1)
|
|
(memory 1 1)
|
|
)
|
|
|
|
(instance (instantiate $m0))
|
|
(instance (instantiate $m0))
|
|
(instance (instantiate $m0))
|
|
(instance (instantiate $m0))
|
|
)
|
|
"#,
|
|
)?;
|
|
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().memories(10).build());
|
|
let err = Instance::new(&store, &module, &[]).err().unwrap();
|
|
assert!(
|
|
err.to_string().contains("resource limit exceeded"),
|
|
"bad error: {}",
|
|
err
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn limit_tables() -> Result<()> {
|
|
let mut config = Config::new();
|
|
config.wasm_module_linking(true);
|
|
let engine = Engine::new(&config)?;
|
|
let module = Module::new(
|
|
&engine,
|
|
r#"
|
|
(module
|
|
(module $m0
|
|
(table 1 1 funcref)
|
|
(table 1 1 funcref)
|
|
(table 1 1 funcref)
|
|
(table 1 1 funcref)
|
|
(table 1 1 funcref)
|
|
)
|
|
|
|
(instance (instantiate $m0))
|
|
(instance (instantiate $m0))
|
|
(instance (instantiate $m0))
|
|
(instance (instantiate $m0))
|
|
)
|
|
"#,
|
|
)?;
|
|
let store = Store::new_with_limits(&engine, StoreLimitsBuilder::new().tables(10).build());
|
|
let err = Instance::new(&store, &module, &[]).err().unwrap();
|
|
assert!(
|
|
err.to_string().contains("resource limit exceeded"),
|
|
"bad error: {}",
|
|
err
|
|
);
|
|
Ok(())
|
|
}
|